Guava TypeToken and generic classes - java

I'm using Guava TypeToken class in my project, but I'm getting an unexpected result.
I have MyGenericClass<T>:
public class MyGenericClass<T> implements MyInterface {
private TypeToken<T> recordType;
public MyGenericClass(String name) {
this.recordType = new TypeToken<T>(getClass()) {};
// ...
}
// ...
#SuppressWarnings("unchecked")
protected Class<T> getRecordType() {
return (Class<T>) recordType.getRawType();
}
}
So if I instantiate an object via new MyGenericClass<String>() and then invoke getRecordType() I expect to get java.lang.String, instead I'm getting java.lang.Object.
But, if I extend generic class:
public class MyStringImpl extends MyGenericClass<String> {
// ...
}
and instantiate this new class: new MyStringImpl() then I get the correct result.
Why is this happening? Is this the expected behaviour of TypeToken?

To add some boring details to Ian's answer: It would be nice if TypeToken worked the way you expected, but this is impossible. When you declare
public class MyGenericClass<T> implements MyInterface {...}
the JVM sees something like
public class MyGenericClass<Object> implements MyInterface {...}
due to erasure.
But when you declare
public class MyStringImpl extends MyGenericClass<String> {...}
then in the definition of MyStringImpl the generics used are recorded and can be obtained via Class#getGenericSuperclass(). That's (a part of) the magic behind TypeToken.

To make this work you need the same anonymous subclass trick when you instantiate MyGenericClass:
new MyGenericClass<String>() {}
If you do that then you will get String returned by getRecordType. The reason why this is necessary is explained in the JavaDocs for that TypeToken constructor.
Clients create an empty anonymous subclass. Doing so embeds the type parameter in the anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.

Related

Java generics & incompatible types using <S extends {class}>

abstract class AbsClass {}
class MyAbs extends AbsClass {}
class MyClass<S extends AbsClass> {
S getObj() {
return new MyAbs();
}
}
Getting compiler issue:
Error:(33, 16) java: incompatible types: MyAbs cannot be converted to S
What is the correct way to do this?
Edit:
I was hoping to be able to intialize MyClass{MyAbs} then call getObj() which would return me a MyAbs object. With Andy's answer, I would have to cast the AbsClass to MyAbs or MySecondAbs which was what I was trying to avoid
Andy has described how to fix the problem, I'll try to explain why.
S is not guaranteed to be assignable to MyAbs, only to AbsClass, and each instance will specify a subclass of AbsClass.
Consider:
class MyOtherAbs extends AbsClass {}
MyClass myClass = new MyClass<MyOtherAbsClass>{};
This would conflict with what you have.
UPDATE:
Based on your comments it looks like you want to achieve the above. The challenge is what if MyOtherAbs has a constructor with arguments? Or is implemented as a Singleton (i.e. a private constructor and static getInstance()). In short, there is no guaranteed unified way you can construct these. Further, due to type erasure, at runtime, the concept of S is gone, so you cannot do something like:
return new S();
You basically have two alternatives, one is to use subclassing:
public interface AbsClassFactory<S extends AbsClass>{ //takes the place of MyClass
public S getInstance();
}
MyAbsFactory implements AbsClassFactory<MyAbs>{
public MyAbs getInstance(){
return new MyAbs(); //or however MyAbs is instantiated
}
}
The other option is reflection:
class MyClass {
public <S extends AbsClass> S getObj(Class<S> clazz) throws InstantiationException, IllegalAccessException {
return clazz.newInstance();
}
}
Note that the second assumes there is a no arg constructor available and will throw a runtime exception if one is not.
S extends MyAbs, not the other way around. any object of type S can be cast into MyAbs, but MyAbs cannot be cast into it's derived classes.
Please explain what you are trying to achieve.
From your comment it seems that you can accomplish the same using the Supplier interface:
Supplier<MyAbs> supp = MyAbs::new; // Constructor reference
MyAbs obj = supp.get();
Supplier<MySecondAbs> supp2 = MySecondAbs::new;
MySecondAbs obj2 = supp2.get();

Java nested genericsType

I got an interesting issue. Consider the following code:
public class GenericsTest
{
// An interface with a generic type.
public interface IObject<K>{}
// An class with a generic type
public static class ObjectA<K>
{
// An inner class without generic type, but implementing the interface with generic Type
// When adding a genericType to this class, it will popup the warning: 'hiding'
public class ObjectB implements IObject<K>
{
}
// A getter with the interface as return Type
public IObject<K> getObjectB()
{
return new ObjectB();
}
}
public ObjectA<String> objectA = new ObjectA<String>();
// This field is yelling for an genericType, though it can't get one because the class doesn't support a generic argument.
public ObjectB genericObject = (ObjectB)objectA.getObjectB();
}
So the issue is that my IDE is complaining about a missing genericType of the genericObject field, and that I should add a SupressWarning annotation to the method. (luckily not code breaking, though still pretty annoying).
I could add a generic type to the inner class, though than it would 'hide' a generic argument, meaning I would need to add a SupressWarning annotation there.
A second fix would be to use a second generic type like <S extends K>. In which case I don't need a SupressWarning annotation at the class. Though when I try to use the getter, my IDE is complaining:
The member type GenericsTest.ObjectA.ObjectB<String> must be qualified with a parameterized type, since it is not static.
So basically I can't use the getter, unless I add an argument of the genericType to the method.
My question is, what is the cleanest way to solve this problem without changing the inner class to a nested class?
Here's a short example that compiles with no issues:
public class Test
{
interface K<T> { }
static class A<T>
{
class B implements K<T> { }
public K<T> getK() { return new B(); }
}
A<String> a = new A<String>();
A<String>.B b = (A<String>.B) a.getK();
}
Notice the last line:
A<String>.B b = (A<String>.B) a.getK();
To be honest, I'm not sure how the example you've given even compiles as far as it does - the class 'ObjectB' is not visible from the main 'GenericsTest' scope, it needs to be prefixed with its' parent class.

Using static nested class as generic parameter

I'm trying to create an interface with a bounded type parameter, with implementations supplying static nested classes to implement this parameter, as follows:
public interface InterfaceProblem<T extends IMyParameter>{
T getParameterInstance();
}
interface IMyParameter {}
class MyClass implements InterfaceProblem<MyParameter> {
public MyParameter getParameterInstance() {
return new MyParameter();
}
class MyParameter implements IMyParameter{}
}
This gives me a compile error "MyParameter cannot be resolved to a type" on the MyClass declaration and its method. This disappears if I move the static class to its own type:
class MyClass implements InterfaceProblem<MyParameter> {
public MyParameter getParameterInstance() {
return new MyParameter();
}
}
class MyParameter implements IMyParameter{}
However, I'd like to avoid that, since the MyParameter implementation is closely related to the MyClass implementation. Is there a better way I can acheive this? Is this correct compiler behaviour? (I'm using Eclipse Mars and Oracle jdk1.8.0_60)
You're missing an import:
import com.example.MyClass.MyParameter;
While the MyParameter type is on scope for the getParameterInstance() method's return type, it is not for the MyClass's binding of <T>

Anyway to pass a generic type in a constructor to a parent class constructor?

Is there anyway to get a generic type in a class constructor in order to pass it to a parent constructor?
Given base class:
public class BaseSupport<T>{
private Class<T> type;
public BaseSupport(Class<T> type){
this.type = type;
}
}
Is there anyway to create a subclass to do this?
public class Support<T> extends BaseSupport<T> {
public Support() {
// is there anyway to know what "T" is here?
super( T.class );
}
}
And then finally, I would simply be able to create a class like:
public class MyClass extends Support<OtherClass>{
// no need to explicitly define a constructor here since the Support class handles it
}
I know Guava has TypeToken to help with retrieve generic type information, but given that super() must be the first method called in a constructor, I can't use it to extract the type information to pass to the parent class.
I suspect that this isn't feasible, but thought I would ask to see if there are any features/tricks that I don't know about in Java 7 since 'T' would be available at compile time.
Did you see the option mentioned in the TypeToken docs?
Capture a generic type with a (usually anonymous) subclass and resolve it against a context class that knows what the type parameters are. For example:
abstract class IKnowMyType<T> {
TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
new IKnowMyType<String>() {}.type => String
You could effectively do this.
public class MyClass extends Support<OtherClass>{
// no need to explicitly define a constructor here since the Support class handles it
public MyClass() {
super(OtherClass.class);
}
}
And in support, have a constructor that accept a Class type and call the super keyword as I have done above (eliminating T.class all together).
Update: Alternatively, you can use Reflection to get ParameterizedType on your BaseSupport class and not need to provide an argument to your BaseSupport public constructor.
Resource:
Reflecting Generics.
Related Answer on StackOverflow.

AbstractFactory with generic types in Java: a design problem

I have the following 2 interfaces accordingly to abstract factory pattern:
public interface GenericObjectInterface<T extends Number>{
public T getResult();
}
public interface AbstractFactoryInterface{
public <T extends Number> GenericObjectInterface<T> createGenericObject();
}
I have an abstract class implementing GenericObject, but it's still unaware of the concrete type (it does only generic operations on Number):
public abstract class GenericAbstractClass<T extends Number> implements GenericObjectInterface<T>{ }
Then I have a series of concrete class extending that perform generic parameter substitution:
public class IntegerObject extends GenericAbstractClass<Integer>{
public Integer getResult(){}
}
....
Now, from inside an implementation of the factory I build the concrete type, that's implementing GenericObjectInterface but has lost it's generic parameter:
public class ConcreteFactory{
public <T extends Number> GenericObjectInterface<T> greateGenericObject(Class<T> c){
if (c.class.isInstance(Integer.class)){
IntegerObject obj = new IntegerObject();
//I would like to return obj
GenericObjectInterface<T> a = new IntegerObject(); //errror
GenericAbstractClass<T> a = new IntegerObject(); //errror
return a;
}else if (c.class.isInstance(Double.class)){
}
}
}
I would like to return obj that implements GenericObjectInterface but I don't know how can I do it.
how can I solve this?
I'm used to abstract factory but I've never used it with generics. Am I doing some mistakes in interpreting the pattern?
If your method returns an IntegerObject why don't you just return GenericObjectInterface<Integer>? You already know the parameter type.
In that case, just add a generic parameter to AbstractFactoryInterface, too:
public interface AbstractFactoryInterface<T extends Number> { ... }
public class ConcreteFactory implements AbstractFactoryInterface<Integer> { ... }
In your implementation the type of T would be inferred from the assignment, and thus you could do this:
GenericObjectInterface<Double> g = new ConcreteFactory().greateGenericObject();
In that case T would be Double but you'd use Integer internally, resulting in this:
GenericObjectInterface<Double> a = new IntegerCell();
Since the compiler can't ensure that T will always be of type Integer it won't allow you to do that assignment.
Abstract factory is characterized by the factory method returning an interface or abstract class reference instead of the concrete reference. It does not extend to type parameters.
Think of it this way: should you be able to do this?
public class ConcreteListFactory {
public <T> List<T> createList() {
return new ArrayList<String>();
}
}
What if the caller wanted a List<Integer>?
If you want your factory to return a generified type, you should have your concrete class accept the type parameter. Otherwise have your factory method return a GenericObjectInterface<Integer>.
Alternatively, you could have your method accept a type token (Integer.class). For example:
public <T extends Number> GenericObjectInterface<T> createGenericObject(Class<T> clazz) {
if ( clazz.equals(Integer.class) ) {
return (GenericObjectInterface<T>) new IntegerObject();
}
}
This will result in an unchecked cast warning but you can prove to yourself that it is safe, and thus suppress the warning or ignore it.
Generally, factories are not implemented as generics because you can't examine the type of the generic to determine the type of object to create (you can't do T.getClass) which is why #Mark's example causes the class to be passed in as an argument.
I think, more usually you would have multiple concrete factories. One for each Number type that you intend to support.
public interface AbstractFactoryInterface<T extends Number> {
public GenericObjectInterface<T> createGenericObject();
}
class IntegerFactory implements AbstractFactoryInterface<Integer>...
class LongFactory implements AbstractFactoryInterface<Long>...
You could then create a Map<Class, AbstractFactoryInterface>...
Map<Class, AbstractFactoryInterface> myMap = ...;
myMap.put(Integer.class, new IntegerFactory());
myMap.put(Long.class, new LongFactory ());
casting is perfectly fine here. if c==Integer.class, then T=Integer, casting GOI<Object> to GOI<T> is absolutely correct. It is a checked cast because you have checked that T=Integer before casting, therefore the unchecked warning can be legitimately suppressed.

Categories