views:

57

answers:

3

Here's an excerpt from my code

package dictionary;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.regex.*;

public class IntelliCwDB extends CwDB {

    public IntelliCwDB(String filename) {
        super(filename);
    }

    @Override
    public void add(String word, String clue) {
        System.out.println("inelli");
    }
}

And CwDB...

package dictionary;

import java.util.LinkedList;
import java.io.*;
import java.util.Scanner;

public class CwDB {
    protected LinkedList<Entry> dict;

    public CwDB(String filename) {
        dict = new LinkedList<Entry>();
        createDB(filename);
    }

    public void add(String word, String clue) {
        System.out.println("cwdb");
        dict.add(new Entry(word, clue));
    }

    protected void createDB(String filename) {
        try {
            BufferedReader f = new BufferedReader(new FileReader(filename));
            while (f.ready()) {
                this.add(f.readLine(), f.readLine());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In the main() part I create a new IntelliCwDB object, which fires the execution of createDB().

The problem is that I want CwDB.createDB() to use it's own CwDB.add() method, not the one from IntelliCwDB. Is there any other neat solution than creating CwDB separately, then passing it into the constructor of IntelliCwDB just to rewrite the LinkedList<Entry> dict database?

+2  A: 

You could solve it like this:

  • Create a private (or final) version of the CwDB.add, lets call it privateAdd.

  • Let the old add method in CwDB call this method instead.

  • Whenever you want to be sure that the CwDB-version of add is used, you simply call privateAdd instead.

Sample code

public class CwDB {

    // ...

    public void add(String word, String clue) {
        privateAdd(word, clue);
    }

    private void privateAdd(String word, String clue) {
        System.out.println("cwdb");
        dict.add(new Entry(word, clue));
    }

    protected void createDB(String filename) {
        // ...
            // "Calling parent method from within the parent class"  :-)
            this.privateAdd(f.readLine(), f.readLine());
        // ...
    }

    // ...
}

As @Péter Török correctly points out: You should never call a virtual method (directly or indirectly) from within a constructor. The reason is simple: The sub-class will get to run code before its super class (and itself) is initialized properly. (Whether or not it applies in this particular example stands to reason though.)

aioobe
Java is ambiguous in that case, because from what I understand, the switch from CwDB.add() to IntelliCwDB.add() in CwDB constructor is done silently (or to be precise - the switch that has not yet been performed). Shouldn't it be better to completely disable such operations? Are they of any real use in normal programming?
krzemian
Hmm.. I'm not sure what you mean. The "switch" is never done. All methods are *always* virtual, from the beginning, even in the constructor. What is it you suggest disabling? Method-overriding? In C++ you have the option of making a method non-virtual (as you seem to want in this scenario). Java took another approach, and made all methods virtual, without the option of disabling it.
aioobe
+1  A: 

I would move the add method to addInternal in CwDB, and make a new add which calls addInternal. Then in the createDB method, call addInternal to get the correct method. Eg.

class CwDB {
   ..
   private void addInternal(String word, String clue) {
     ..
   }

   public void add(String word, String clue) {
     addInternal(word, clue);
   }

   public void createDB(String filename) {
      ..
      addInternal(w, c);
      ..
   }
}
andrewmu
You need to mention that he should make the internal add private to make sure it's not overridden.
aioobe
True, I've done that in the example code.
andrewmu
+2  A: 

You experienced one of the reasons why one should not call virtual methods from a constructor. For more details on this, see Effective Java 2nd Edition, Item 17: Design and document for inheritance or else prohibit it.

The simplest solution to your problem would be to split the base class method into a nonvirtual (final and/or private) one, and another, virtual, method, which calls the former in the base class implementation.

@aioobe was faster to provide an example to this :-)

Péter Török
Most of the nice abstract questions are about who is quickest on the draw :)
andrewmu
"quickest on the draw",, hahah
aioobe