Reputation: 1390
This seems like a newbish question, but the last time I worked with Java, the language didn't have generics. I have a class hierarchy (names changed to be as generalized as possible):
public abstract class AbstractBase { .... }
public class ConcreateSubA extends AbstractBase { .... }
public class ConcreateSubB extends AbstractBase { .... }
...
public class ConcreateSubZZ9PluralZAlpha extends AbstractBase { .... }
...
I'm trying to clean up some legacy code, and there's one place where a ton of repetitive duplication can all be factored out into a single routine via generics. (I'm thinking generics because when this routine is called, it needs to operate on only one of the concrete classes.)
The routine looks like
public <Thing extends AbstractBase> void someFunc()
{
another_function_call (Thing.concreteSpecialToken);
// could also be
// another_function_call (Thing.concreteSpecialToken());
// if methods are more feasible than fields
// Cannot use
// another_function_call (Thing().concreteSpecialToken());
// because creating instances of these types is a Major Operation[tm]
}
I'm leaving out about a zillion lines, but that's the important part: someFunc()
is type parametric (it actually takes arguments but none of them are Things so no inference). Eventually I need to fetch a special token and this is where I'm getting fuzzy.
The tokens are huge-ass unique strings for each concrete class. They're class-based, not instance-based. The actual token value is declared as a private static final
field in each subclass.
So I need to use the public methods/fields of a base class to (eventually) get to the private static field of a subclass. Obviously I can't declare an abstract static
method in the base, because that makes no sense. If the data were instance-based, then this would be trivial, with a polymorphic getter in the base class, but the subclass stuff is static.
I feel like I'm missing a feature of Java generics here, but I can't use Thing.whatever()
unless the whatever
is something that's possible to declare in the abstract base class. I'm running up against either limitations of Java or my lack of expertise trying to bridge the gap. The one attempt I made that seemed promising also had a ton of code duplication all the way down the class hierarchy, defining abstract methods with the exact same code over and over... which is what generics are supposed to help prevent!
Upvotes: 9
Views: 1649
Reputation: 4571
If you want to keep the token as it is, a private static final
field, you can get it through reflection:
public <Thing extends AbstractBase> void someFunc(Class<Thing> clz)
{
try {
Field field = clz.getField("concreteSpecialToken");
field.setAccessible(true);
Object concreteSpecialToken = field.get(null);
another_function_call (concreteSpecialToken);
} catch (IllegalAccessException e) {
handle(e);
} catch (NoSuchFieldException e) {
handle(e);
}
}
At call site, you have to do someFunc(ConcreateSubZZ9PluralZAlpha.class)
. But I wonder if you can do that, why don't you just pass the token object as a parameter, as in someFunc(ConcreateSubZZ9PluralZAlpha.concreteSpecialToken)
? Or maybe even move the method someFunc()
to the token class itself.
Upvotes: 1
Reputation: 122414
A bit clunky but if someFunc
took a Class<Thing>
parameter you could use reflection:
public <Thing extends AbstractBase> void someFunc(Class<Thing> clz) {
// exception handling omitted
Object whatever = clz.getDeclaredMethod("whatever").invoke(null);
But you could maybe take better advantage of polymorphism by using nested classes, something like
public abstract class AbstractBase {
public static class Info {
public String getInfo() {
return "AbstractBase";
}
}
}
public class ConcreteSubA extends AbstractBase {
public static final Info INFO = new Info() {
public String getInfo() { return "ConcreteSubA"; }
}
}
and have someFunc
take an AbstractBase.Info
parameter.
public <Thing extends AbstractBase> someFunc(AbstractBase.Info inf) {
String info = inf.getInfo();
}
// call it as
ConcreteSubB csb = someFunc(ConcreteSubB.INFO);
The idea is that each class in the hierarchy has a singleton instance of Info
holding its formerly-static data.
Upvotes: 2
Reputation: 1502985
I'm running up against either limitations of Java or my lack of expertise trying to bridge the gap.
It's a limitation of Java, although a fairly reasonable one IMO. Basically you're still trying to use static members as if they were polymorphic, and that's not going to work - generics don't help you there.
Options:
Class
for Thing
unless you pass it in explicitlyThing
anyway, just make it an abstract instance member, which in each implementation happens to return the value of a static fieldUpvotes: 7
Reputation: 49804
If I understand it correctly, you need the concrete class of the type parameter. The usual way of doing it is to declare your method like this: public <T extends AbstractBase> void someFunc(Class<T> clazz)
This of course means an extra parameter needs to be passed to the method and you need to use reflection to access the static fields, but given Java's type erasure it's the only way.
The moral of the story is that generics and statics don't go very well together.
Upvotes: 3