Java - Instantiating objects of type X when all you have is the reference type
I had a quick look at the "Related Questions" suggested but I couldn't find one directly related to what I'm asking. Even if there was one, I'd still appreciate your opinion on the best way to do this.
First some context.
I'm working on extending a Java Application and I have the following Interface and Class:public interface Mapper {
public void foo();
}
public class SomeGrammar implements Mapper {
public SomeGrammar() {}
public SomeGrammar(SomeGrammar toCopy) {
//Copy specific fields
}
public void foo() {}
}
The application I'm using was built with only one grammar type in mind, SomeGrammar
, and as such the SomeGrammar
class is woven throughout through the application.
SomeGrammar
objects are often copied using a copy constructor:
SomeGrammar aGrammar = new SomeGrammar();
SomeGrammar newGrammar = new SomeGrammar(aGrammar);
I'm attempting to implement new types of grammars. So I was going to pull the core methods out and implement a Grammar
interface, or even consolidate those methods into the Mapper
interface. So all grammar classes would implement this interface.
Anyway, my question to you is, what is the best way to go about replacing the above line so that I can gene开发者_高级运维ralise the copying of whatever grammar object happens to be currently being referenced by the Interface's Type?
Obviously I can't
Grammar newGrammar = new Grammar(aGrammar);
since Grammar
is the Interface's Type, and new Grammar(aGrammar)
makes no sense. I suppose I could add a cloning method to my interface to use instead of a copy constructor, but as I said before, I'd appreciate your opinion on the matter.
Thanks in advance,
EoinIf I understood your question correctly, my solution would look something like the following :
Lets say we had 3 concrete grammars we wished to create, GrammarA, GrammarB, and GrammarC. We would define each of those classes to implement the Grammar interface. The GrammarInterface would have your foo() function, and also a doCopy() function defined. Each of those concrete Grammar classes would then be responsible for implementing the foo() capability and for returning a copy of themselves by calling a copy constructor. Is this what you were looking for?
interface IGrammar {
public void foo();
public Grammar doCopy();
}
class GrammarA implements IGrammar {
public Grammar doCopy(Grammar g) {
return new GrammarA(g);
}
}
Object.clone() is bad, see Effective Java for discussion.
Marker interfaces are bad (see Externalizable, Serializable, ...) there are tons of libraries out there which try to circumvent such useless marker interfaces.
Cloneable<T>
might be an approach.
I'd suggest a Factory
:
Grammar someGrammar = Factory.newGrammar();
Grammar copyOf = Factory.copyGrammar(someGrammer);
The reasons for that is, the Factory can delegate to the grammar object if it's Cloneable, but it can do other things as well such as keep a list of creates grammars or returning adapters or returning cached instances (in general, stuff which is independent of the actual grammar implementation). Also, the implementation classes need not to be used directly throughout the whole application.
You can add a clone()
as part of your interface, and have the concrete classes implement it properly. Then your client code will look like:
Grammar aGrammar = new SomeGrammar();
Grammar newGrammar = aGrammar.clone();
Check out a bit of explanation here. Personally, I would just recommend having your own copy()
method as part of Grammar
interface. It's better than reusing clone()
IMHO.
I think what you want is to have the Grammar
interface include a makeCopy
signature.
Then making a copy would look like:
Grammar newGrammar = oldGrammar.makeCopy();
Avoid using the clone
method, for that way lies madness!
You could also use reflection to find the type of Grammar and create a new instance of this type using the matching constructor:
interface Interface {
}
class A implements Interface {
public A(Interface other) {
}
}
class B implements Interface {
public B(Interface other) {
}
}
public class Test {
public static Interface newWithExactType(Interface i) throws Exception {
Class<? extends Interface> c = i.getClass();
Constructor<? extends Interface> ctor = c.getConstructor(Interface.class);
return ctor.newInstance(i);
}
public static void main(String[] args) throws Exception {
Interface a = newWithExactType(new A(null));
Interface b = newWithExactType(new B(null));
System.out.println("Type of a: "+a.getClass().getSimpleName());
System.out.println("Type of b: "+b.getClass().getSimpleName());
}
}
If you really need the ability to copy between multiple different implementations, I would suggest the following approach:
public interface Mapper {
void Mapper();
void Mapper(Mapper copy);
Foo getFoo();
Bar getBar();
// getters for shared properties which can be copied between implementations
void doStuff();
}
public abstract AbstractBaseMapper implements Mapper {
protected Foo foo;
protected Bar bar;
public void Mapper() {}
public void Mapper(Mapper copy) {
foo = copy.getFoo();
bar = copy.getBar();
}
public Foo getFoo() { return foo; }
public Bar getBar() { return bar; }
}
public ConcreteMapper extends AbstractBaseMapper {
public void doStuff() {
}
}
public AnotherMapper extends AbstractBaseMapper {
public void doStuff() {
}
}
public YetAnotherMapper implements Mapper {
// NB: this one is a straight implementation of the Mapper interface, doesn't inherit from AbstractBaseMapper...
}
Now you can do stuff like:
Mapper m1 = new YetAnotherMapper();
Mapper m2 = new ConcreteMapper(m1);
Mapper m3 = new AnotherMapper(m2);
The downside to this approach is that you'd have to define all the copyable fields in the base interface so that they are always guaranteed to be implemented. An alternative would be to use reflection as suggested by Karl. You can also look into Dozer which automates such reflective property copying.
IMO the best option is to get to have
public interface Mapper extends Cloneable {
public Object clone();
}
And have that method call super.clone()
and perform deep cloning if relevant (only the implementation class knows about that, in principle).
Resorting to reflectivity will probably hit performance and make extension design a bit more (too?) complex. Another option here would be to provide with an abstract class that features the copy constructor instead of an interface, but that has obvious limitations that could be a show stopper.
精彩评论