Java nested genericsType - java

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.

Related

Can generics infer a related type?

Given
public class Foo {
public static class FooBuilder { ... }
}
I want to write a method on a third class that returns Foo, given Foo.FooBuilder.class
i.e.
Foo f = x.make(Foo.FooBuilder.class, someData);
Is it possible to declare a signature using generics that can imply the return type? Is there some language feature that lets me say "type U is outer class of type T"?
Obviously, it is possible to specify that type extends, or is the base of, a generic type (U extends T or U super T, respectively) but I am looking for U outer T which is, I think, more than Java can offer, even indirectly, at least in 1.7, which I am targeting.
So far, I have simply declared both inner and outer types, which works but is a wider definition than I am after and looks clumsy too.
public <TYPE,BUILDER> TYPE make(Class<BUILDER> builderClass, Map<String,Object> data) {
// Construct TYPE
}
Is there a way to infer TYPE without explicitly providing a template parameter?
There is a Class#getDeclaringClass method that may work in your case.
Quoting the docs:
If the class or interface represented by this Class object is a member of another class, returns the Class object representing the class in which it was declared.
EDIT:
After the clarification of OP, here is the new suggestion:
You create an generic interface to mark all your nested classes:
public interface Nested<P> {
}
Then you apply it to your Foo.Bar class like this:
public class Foo {
public static class Bar implements Nested<Foo> {
}
}
Then in your factory you can have the following:
public <P> P make(Class<? extends Nested<P>> clazz, Map<String, Object> someData) {
// do whatever you need to do
return (P) clazz.getDeclaringClass();
}
However, with this construct, there is not way to validate it your nested class is the real class, declared when implementing the generic interface.

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.

Guava TypeToken and generic classes

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.

Passing parameterized Class instance to the constructor

I have lost in the Jungle of Generics, please help me :) I have something like this:
public class BaseClass<TYPE> {
public BaseClass(Class<TYPE> clazz) {};
}
public class FirstLevelClass<REFRESHABLE
extends RefreshableInterface> extends BaseClass<REFRESHABLE> {
public FirstLevelClass(Class<REFRESHABLE> clazz) {
super(clazz);
};
}
public class Argument<T extends AnyOtherClass>
implements RefreshableInterface {
public refresh() {}
}
pulbic class ProblematicClass
extends FirstLevelClass<Argument<AnyOtherClassDescendant>> {
public ProblematicClass() {
//Compiler error: Constructor
//FirstLevelClass<Argument<AnyOtherClassDescendant>>(Class<Argument>) is undefined
super(Argument.class);
}
}
As far as I think, the compiler should accept Argument since it implements RefreshableInterface.
Why do I get this error?
How can I make the ProblematicClass working?
ps: if you have better title for this, please change it. I could not make up better.
Issue is, your constructor expects a Class<T>, and T in your code is inferred as Argument<AnyOtherClassDescendant>.
So, you should pass a Class<Argument<AnyOtherClassDescendant>>, and you're passing Class<Argument>. But you can't pass that Class instance directly, as you cannot do Argument<AnyOtherClassDescendant>.class.
You can however, solve the issue by typecasting the class to required instance:
public ProblematicClass() {
super((Class<Argument<AnyOtherClassDescendant>>)(Class<?>)Argument.class);
}
Note, how you've to typecast Class<Argument> first to Class<?>, and then the resultant type to Class<Argument<AnyOtherClassDescendant>>. There is no simple way to achieve that.
The reason behind this is, there is only a single Class instance for all parameterized instantiation of a generic type, that is associated with the class itself. A single compilation unit of a generic type, compiles to just a single class file. I guess this is different in how C++ implements templates. There you get different machine codes for different instantiation.
So, if you execute the below code, you'll get true as output:
List<String> strList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();
boolean isSameClassInstance = strList.getClass() == intList.getClass();
System.out.println(isSameClassInstance);

Overriding non-parameterized classes nested in a parameterized outer class in Java

Can someone please help with the syntax of subclassing a non-parameterized nested class inside a parameterized outer class, like the following?
public abstract class Foo<T> {
public class Bar {
Set<T> aSet;
}
abstract class Baz {
abstract void doSomething(Map<? extends Bar, T> aMap);
}
}
Then in a separate file, I'm not entirely sure how to define the type variable S here without parameterizing ConcreteBaz. And I can't use a wildcard variable like extends Foo<?>.Baz (I think) because I need access to that generic type variable in the doSomething method:
public class ConcreteBaz extends Foo<S>.Baz { // compilation error
public ConcreteBaz(Foo<S> foo) { foo.super(); }
public void doSomething(Map<? extends Foo<S>.Bar, S> aMap) { ... }
}
Can someone please help me figure out a way around this? Thanks.
Declare S as a type parameter:
public class ConcreteBaz<S> extends Foo<S>.Baz {
Otherwise the compiler will think S is a concrete type instead of a type parameter.
Complete example:
public class ConcreteBaz<S> extends Foo<S>.Baz {
public ConcreteBaz(Foo<S> foo) {
foo.super();
}
#Override
void doSomething(Map<? extends Foo<S>.Bar, S> aMap) {
// ...
}
}
Your problem is that the nested class is nonstatic. I'll check it out, but I'm pretty sure you cannot subclass such classes, except maybe if nested in the same class, or when creating an anonymous type.
Is declaring the nested class static viable? Them it would definitely work.
Edit: scratch all that. I don't have a compilation error. What is S in your case? You do realize you have to pass a concrete class to Foo, and cannot leave it parametrized with an unknown parameter S?
If ConcreteBaz needs to refer to the type parameter, that implies that it needs the type parameter itself:
public class ConcreteBaz<S> extends Foo<S>.Baz {

Categories