The below code gives me the error:
SceneNode.java:17: cannot find symbol
symbol : method execute() location:
class java.lang.Object
operation.execute();
^ 1 error
Code:
import java.util.LinkedList;
import java.util.Iterator;
public class SceneNode<T>{
T operation;
public SceneNode() {
}
public SceneNode(T operation) {
this.operation = operation;
}
public void setOperation(T operation) {
this.operation = operation;
}
public void doOperation() {
operation.execute();
}
}
It's a cut down (for your readability) start of a simple scene graph. The node could be a model, transformation, switch, etc., so I made a variable called operation
that's type is开发者_高级运维 defined by the T
class variables. This way I can pass a Transformation
/ Model
/ Switch
object (that has an execute
method) and pass it like this:
SceneNode<Transformation> = new SceneNode<Transformation>(myTransformation);
I'm pretty sure having a base class of SceneNode
and subclassing for all the various types of nodes would be a better idea (I was trying out generics, only learnt about them recently). Why doesn't this work? I must be missing something fundamental about generics.
It doesn't work because T
could be any type, and Java is statically typed. The compiler has no idea whether you'll try to create a SceneNode<String>
- then what would execute
do?
One option is to create an appropriate interface, e.g.
public interface Executable {
void execute();
}
and then to constrain T
in SceneNode
to implement Executable
:
public class SceneNode<T extends Executable> {
...
}
(I find it a little bit odd that T
has to extend Executable
rather than implement it in the source code, but then T
could end up being an interface itself, so I guess it makes sense.)
Then it should work fine. Of course you could make Executable
an abstract superclass instead - or even a (non-final) concrete class - if you wanted, but I would generally prefer to use an interface unless I had some reason not to.
I'm guessing you come from a C++ background.
The compiler has no idea what kind of a thing T might be because you haven't told it.
If you had an interface called, for example, Executable
which defines your execute()
method, then you would need to do:
public class SceneNode<T extends Executable> {
// ...
}
Now, the compiler will know that T is an Executable
, and will give you access to all the methods on that interface.
Recently I came across a situation where I had to call a method on generic object. Getting reflection in action worked for me.
public class SceneNode<T>{
T operation;
public SceneNode() {
}
public SceneNode(T operation) {
this.operation = operation;
}
public void setOperation(T operation) {
this.operation = operation;
}
public void doOperation() {
Method m = operation.getClass().getMethod("execute");
m.invoke(operation);
}
}
Java is statically typed language. You must know the type at compile-time in order to be able to invoke a method. Instead of a subclass you can have an interface Executable
that defines the execute()
method. T
(without any <T extends SomeClass>
) has only the methods defined by java.lang.Object
.
精彩评论