0

Let's say we have a class with the following method:

public class Entry {
    private String name;

    public static Entry getOrCreate(String name) {
        // ...
        return new Entry(name);
    }
}

This class may be subclassed (e.g. SubEntry), and the logic behind "getOrCreate" does not change. But the subclasses should not return a new object of the type Entry, but of the type of the respective subclass (e.g. return SubEntry(name))

How can I realize this without reimplementing the method getOrCreate for every subclass of Entry? Is there a term for this kind of technique?

5
  • 1
    I think generics will get you there. See docs.oracle.com/javase/tutorial/java/generics/types.html Commented Feb 4, 2015 at 20:06
  • 1
    @ochi, generics + reflection to be more precise, as he will need reflection to create the instance if using generics Commented Feb 4, 2015 at 20:07
  • @AndyBrown fair enough! I am just not sure how rigid the requirement for the method to be static is. Also, you can always return an Object right? Commented Feb 4, 2015 at 20:11
  • This could help: stackoverflow.com/questions/14353620/… But really it could be easier to just add the static method to each subclass. If there's really a lot of repeated code you might be able to factor it out into a helper function in the base class. Commented Feb 4, 2015 at 20:19
  • How are you looking to create a SubEntry object, do you want to call: Entry.getOrCreate(name), or SubEntry.getOrCreate(name), or new SubEntry(name)? Commented Feb 4, 2015 at 20:24

3 Answers 3

1

Subclassing Entry does not affect the getOrCreate method because static methods are not part of a class instance; they do not logically belong in any class.

If you instead move getOrCreate into a non-static Factory class, you can use some Generics magic to determine the returned type:

public class Entry {
    private String name;
}

abstract class AbstractEntryFactory<T extends Entry>
    public abstract T getOrCreate(String name);
}

public class EntryFactory extends AbstractEntryFactory<Entry>
    @Override
    public Entry getOrCreate(String name) {
        // ...
        return new Entry(name);
    }
}

public class SubEntryFactory extends AbstractEntryFactory<SubEntry>
    @Override
    public SubEntry getOrCreate(String name) {
        // ...
        return new SubEntry(name);
    }
}

Actually calling the getOrCreate would look different from what it would look like with your code. Instead of this:

Entry myEntry = Entry.getOrCreate("my name");

It would instead look like this:

Entry myEntry = new EntryFactory().getOrCreate("my name");

Or this:

SubEntry myEntry = new SubEntryFactory().getOrCreate("my name");
Sign up to request clarification or add additional context in comments.

Comments

0

Assuming you wanted to be able to call Entry.getOrCreate() to create a type of SubEntry, you'll have to pass along some extra information. The reason is that the getOrCreate() method is not inherited by SubEntry, since it is a static method. So if you want to call it the way I mentioned, you'll have to pass along the class name that you want to create. In the code below there are no checks to validate that Class clazz is an Entry or a subtype, but this gives you a start.

import java.lang.reflect.Constructor;

public class TestClass {
    public static void main(String[] args) {
        Entry entry = (Entry)Entry.getOrCreate("entry", Entry.class);
        SubEntry subEntry = (SubEntry)SubEntry.getOrCreate("subEntry", SubEntry.class);

        System.out.println("entry class: " + entry.getClass().getName());
        System.out.println("subEntry class: " + subEntry.getClass().getName());
    }
}

class Entry {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static Object getOrCreate(String name, Class clazz) {
        // If a constructor is created that takes a String, such as "public Entry(String name)",
        // then each sub class will need to implement that method. Instead I used a getter and
        // setter for the name attribute.
        try {
            Entry entry = (Entry)clazz.newInstance();
            entry.setName(name);
            return entry;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

class SubEntry extends Entry {
}

The end result is this output:

entry class: Entry
subEntry class: SubEntry

Comments

0

There are two questions you are asking:

  1. How do I do this?
  2. What is this technique called?

The second one is much more important than the first.

It seems to me like what you are trying to achieve is similar to the concept of cloning (link) or virtual constructor. But you would like this to be a static method, which raises the question as to why? Since a static method is tied to a certain class, not an instance, you should call it through that class in which case you may just as well explicitly be calling new. But having searched for "retrive class in static context" I would say it is not possible to do exactly what you wrote in the question.

If you convert the static method to a normal method, this can be done by using reflection:

class Entry {
    private String name;
    public Entry(String name) {
        this.name = name;
    }
    public Entry() {
        this.name = null;
    }
    public Entry getOrCreate(String name) {
        try {
            return getClass().getConstructor(String.class).newInstance(name);
        } catch (Exception e) {
            return new Entry(name);
        }
    }
}

class BetterEntry extends Entry {
    public BetterEntry(String name) {
        super(name);
    }
    public BetterEntry() {
        super();
    }
}

You would then be calling the function from an instance, like so:

Entry a = new Entry().getOrCreate("First");
Entry b = new BetterEntry().getOrCreate("Second");
Entry c = b.getOrCreate("Third");

The dynamic types of a, b, c are Entry, BetterEntry and BetterEntry. You could leave out the default constructors, but I added them to make calling getOrCreate feel more like a static method.

If you really want the method to be static, the simplest way would be to just reimplement this function in every subclass.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.