Reputation: 381
I have the following pseudo-code in java:
class MyClassSuper{
public static final String VALUE = "e";
}
class MyClassSub extends MyClassSuper{
public static final String VALUE = "f";
}
class MyGenericClass<T extends MyClassSuper> {
public void print(){
System.out.println(T.VALUE);
}
}
When I create a new instance of MyGenericClass with MyClassSub as its type I'm expecting the print method to print "f", but it prints "e" instead. Is it possible to have MyGenericClass treat T as the actual type that it is while keeping the super class that T extends currently ?
EDIT:
Actual code (abbreviated):
public class Bean implements Serializable {
public static final String FILE_EXT = "";
private static final long serialVersionUID = 1L;
}
public class UserBean extends Bean{
public static final String FILE_EXT = "u";
private static final long serialVersionUID = 1L;
private String name;
public UserBean(String name){
this.name = name;
}
}
public class BeanPersistence<T extends Bean> implements IBeanPersistence<T> {
public void printExtension(){
System.out.println(T.FILE_EXT);
}
}
That is the code I'm trying to write. Basicly I want the BeanPersistence class to know what extension to use for the file it is writing, based on the type of Bean it was initialized with. From the comments I gather this is the wrong way of doing this, what would be the appropriate way ? (Obviously the printExtension method is just there to test)
Upvotes: 2
Views: 1634
Reputation:
This might be suitable, depending on your case.
public class BeanPersistence<T extends Bean> {
public void printExtension(Bean bean) {
System.out.println(bean.getExtension());
}
public static void main(String[] args) {
BeanPersistence<Bean> persistence = new BeanPersistence<>();
UserBean userBean = new UserBean();
AdminBean adminBean = new AdminBean();
persistence.printExtension(userBean); // prints u
persistence.printExtension(adminBean); // prints a
}
}
abstract class Bean {
abstract String getExtension();
}
class UserBean extends Bean {
private static final String FILE_EXT = "u";
@Override
String getExtension() {
return FILE_EXT;
}
}
class AdminBean extends Bean {
private static final String FILE_EXT = "a";
@Override
String getExtension() {
return FILE_EXT;
}
}
In general you should access object properties through methods, not directly. When we call methods, object-oriented behaviour (polymorphism, dynamic dispatch) will invoke the right method.
Upvotes: 2
Reputation: 37645
I did not realise before today that this would even compile. What is happening is that because T extends MyClassSuper
, when the class is compiled into bytecode, the T
is treated as MyClassSuper
, so T.VALUE
is really just MyClassSuper.VALUE
, which is why it prints "e"
. (I'm amazed you are allowed to access static fields and methods on a type parameter - I can see absolutely no reason for it).
Next, remember that overriding only works with instance methods, not with fields or with anything static.
Another problem with what you are trying to do is that due to type erasure, the type T
is not even known at runtime, so you cannot select a String
based on its value.
One solution to your problem is to use an instance method for the extension which can be overridden in the subclass. This requires holding an instance of T
or passing it as a parameter (see the other answers).
Upvotes: 1
Reputation: 140319
The correct approach is to use polymorphism: add a non-static method in MyClassSuper
, which can be overridden in subclasses, and provide an instance to MyGenericClass
:
class MyClassSuper{
public String getValue() { return "e"; }
}
class MyClassSub extends MyClassSuper{
public String getValue() { return "f"; }
}
class MyGenericClass<T extends MyClassSuper> {
private final T instance;
MyGenericClass(T instance) { this.instance = instance; }
public void print(){
System.out.println(instance.getValue());
}
}
Upvotes: 2