I am having some (philosophical?) problems with java's generics...
Consider the following
public interface ClassA<I> {}
public class Obj implements ClassA<String> {}
public interface ClassB<I, T extends ClassA<I>> {
public I getSomething();
public T getAnotherThing();
}
This compiles and works ok, if an object of ClassB is instantiated as:
ClassB<String, Obj<String>> o = new ClassB<>();
o.getSomething(); // <-- Returns String
I was wondering if there is a way to avoid instantiating a ClassB object without having to specify the String generic as well, since it's already inside ClassA. More specifically, I'd like to use it as:
public interface ClassB<T extends ClassA<I>> { // <-- Won't compile
public I getSomething();
}
ClassB<Obj> o = new ClassB<>();
o.getSomething(); // <-- Returns String from Obj declaration, which uses ClassA<String>
I know the "problem" can be easily gotten over with the first example, but I wanted to know if there's a way of reference the actual type of the generic in the implementing ClassA object without having to pass it again in ClassB's declaration, and if not, why.
Hope I was clear enough with my dilemma.
Thanks
I think the answer is "No". :)
Java doesn't provide a way to extract a type argument from a type itself, only from an argument having that type.
Eg:
class ClassB<T extends ClassA<?>> {
<I> I getSomething(ClassA<I>) { ... }
}
... works fine and could be passed an instance of T in order to extract I. But to remove the parameter from the method (which is used only to determine the type for I) you'd need a way to determine I from T itself, and there is no such way.
In general, type inference in Java is limited to establishing a relationship between parameter types and/or between parameter and return types.
The other way, then, would be to declare I and T together as part of one generic parameter, as you tried:
public interface ClassB<T extends ClassA<I>>
But then, as you noted, this is not accepted either. Java's syntax would need to be expanded in order to allow this.
Related
Initial Situation:
I created a factory interface for some other generic interface A<T>:
public interface Factory<T, X extends A<T>> {
public X create();
}
Problem Description:
Now i have the problem that i need to instantiate a factory for every single type T. This becomes very unhandy, especially if some code wants to do some transformation from A<T> to A<T2>, where T2 is some previously unknown type or there are so many different T2, that i do not want to specify hundreds of factories for each special case.
The Goal:
I would like to pass the type T as a generic parameter of the create method. Such that i get something like (Attention, not correct Java :-) ):
public interface Factory<X extends A<?>> {
public <T> X<T> create();
}
An implementation of Factory might then simply do something like:
public class Factory4B implements Factory<B> {
public <T> X<T> create() {
return new B<T>();
}
}
Writing the above version of the interface down gives the error message for the return value of create:
The type X is not generic; it cannot be parameterized with argument
The Question:
Is there some way to realize such a generic factory, or do i need to use a totally different approach? I want to be able to specify X at the class level, such that i can instantiate X easily. I do not want to create a single Factory for every parameter T
Thank you for your time to answer this question in advance.
Rethinking the problems based on the below comments (27.4.15)
Rethinking the problem based on the comments below, my question is not possible to realize as it is not possible to guaranty that the generic parameters are still present in the subclasses of A<T>, or in other words: there is no inheritance of generic parameters.
Example:
Lets think of a class C which is derived from A<String>.
It is not possible to create C<Integer> as C has no type argument.
Therefore, there exist no general Factory, that might create C<Integer>
No, this is not possible with generics alone. Due to "type erasure" the type of T is not there anymore at runtime, so Java doesn't know what constructor to call.
You'll have to find a way to pass the type of T to your class at runtime. In, Java you can use the class Class<S> as a runtime type token. Then your code might look something like this:
public <T> X<T> create(Class<T> classOfT) {
T t = classOfT.newInstance();
return new X<T>(t);
}
In Scala, you can define a class as this:
class MyClass[T1[T2]] {
def abc(a1: T1[Double]):T1[Int] = ???
}
In this code, type parameter T1 represents a type that needs one type parameter T2, so we can create an instance like new MyClass[List].
I want to do this in Java, but I don't have any idea.
I wonder if it is possible in Java, and if not, any idea how I can do this thing in Java.
From my understanding, generic type essentially makes a function of types. So if you have a class like List<T>, you can think of the class List a function of type, so List takes a type parameter like Integer, then it will be a concrete type like list of integers(List<Integer>).
MyClass above takes a type parameter T1, but I want this T1 is also a generic type that takes a type parameter T2, so I can create an instance like MyClass<List> and can use type like List<Integer> or List<Double> inside MyClass. In Scala, if you try MyClass[Int] or MyClass[String] will fail because Int or String does not take a type parameter.
Of course, this may not be necessary if I allow to duplicate some codes, but to make a more general code, I think it is indispensable.
The only way for this to work in Java is to have a common interface for T1.
public interface GenericType<T> {
// ...
}
Then you can define your class as:
class MyClass {
public GenericType<Integer> abc(GenericType<Double> a1) {
//...
}
}
Note that you don't need any more type parameters at the class level anymore because of the common interface. Then before invoking MyClass.abc you would need to wrap the instance you are passing in GenericType.
You can also go one abstraction level higher and define
class MyClass<T1,T2> {
public GenericType<T1> abc(GenericType<T2> a1) {
//...
}
}
This would give you some more flexibility in using MyClass. But that is heavily dependent upon how MyClass is actually implemented.
I'm developing a web-app in Java language, which is composed by a system and some modules. All of them implement the IAppIdentifier interface and I have all the module references and the system itself stored in a List into the system.
The idea is to design that in such way that every module will be able to access the system itself or another modules if they have the required interface (extended from IAppIdentifier), so they have to ask the system for them.
I have this code which works:
#Override
public IAppIdentifier moduleByClass(Class<? extends IAppIdentifier> clazz) {
List<IAppIdentifier> iApps = this.get_Iapps();
for (IAppIdentifier iApp : iApps) {
if (clazz.isAssignableFrom(iApp.getClass())) {
return iApp;
}
}
return null;
}
Basically it's checking that each class from the array is assignable from the required interface and if it is it will return that instance. However the matter is that I have to cast it when it's returned by the method.
For example I have to implement something like that to obtain system's instance:
((ISystem) this.get_Service().moduleByClass(ISystem.class))
My question is, is there any way in java to avoid doing that casting again, ergo, to ensure it will return the same type I'm passing as argument at compile time?
Change method signature to this one :
public <T extends IAppIdenfitier> T moduleByClass(Class<T> clazz)
This should work.
Even if your interface isn't generic you can still use generics in methods for they own purpose. By this code you provide generic rule that T has to be IAppIdentifier itself or has to extend it. Your method now will return object of type T and take as param class as Class<T>.
Then in your code whenever you invoke method moduleByClass you don't have to cast it, for example:
ISystem = this.get_Service().moduleByClass(ISystem.class);
Cast won't be needed here and everything will compile.
There is more info needed according to #XtremeBiker good comment. Inside moduleByClass method it's needed to cast resulting type to T. So it was:
return iApp;
But now it should be:
return clazz.cast(iApp);
Anyway it's still less annoying to make cast in on place inside method body than doing it everytime when that method is invoke.
I have some problem with generics, let me explain.
I have a class which wraps a LinkedList:
public class IdnList<T extends Numerable> extends IdnElement implements List<T> {
private LinkedList<T> linkedList;
#Override
public boolean add(T e){
return linkedList.add(e);
}
//stuff
}
please note that the generic type T of this class extends the Numerable interface.
Ok, now inside a different class I want to invoke this method as follows:
if(childToAdd instanceof Numerable)
((IdnList<?>)this).add((Numerable)childToAdd);
but eclipse says: The method add(capture#1-of ?) in the type IdnList<capture#1-of ?> is not applicable for the arguments (Numerable), and I really can't figure out why it can't work. Why can't I add a Numerable object to my list?
What am I missing?
EDIT:
it's a classic. You ask, and then you find a clue. It seems a workaround is:
((IdnList<Numerable>)this).add((Numerable)childToAdd);
but I don't know how elegant it is. I really appreciate further comments.
Say you have classes A and B that both extend Numerable. Then there are three valid types of IdnList: IdnList<A>, IdnList<B>, and IdnList<Numerable>.
I hope you would agree that you shouldn't be able to add any Numerable to an IdnList<A>.
Now, in this line of code, how does the compiler know whether you've matched up the types correctly?
(IdnList<?>)this).add((Numerable)childToAdd);
All it knows is that childToAdd is a Numerable, and that this is some kind of IdnList. It doesn't know what kind, so it can't guarantee type safety. Remember that generic type checking is done entirely at compile time, not runtime.
I see how the workaround allows the code to compile, but I'm not sure what the risks of it are. It seems to me that since the generic type parameters are erased at runtime, essentially you're just bypassing all type checking here.
The problem lies in the reference to the instance of the IdnList in your other class.
I cannot see it in your code example, but it appears that it does not have the proper type associated with it, especially not when it is being cast to a wildcard.
Since the IdnList requires a type (T) set per instance, there is no way for the compiler to know what you are trying to do.
If your reference to your IdnList has the proper type associated with it, it will accept any subclass of T, like so:
IdnList<Numerable> list = new IdnList<Numerable>();
list.add(new Numerable());
list.add(new AnotherType());
list.add(new YetAnotherType());
(Given that AnotherType and YetAnotherType are subclasses of Numerable)
Since you do not have any type set on the reference to your instance, I am guessing that what you are actually trying to do is this:
public class IdnList extends IdnElement implements List<Numerable> {
private LinkedList<Numerable> linkedList;
#Override
public boolean add(Numerable e) {
return linkedList.add(e);
}
}
How can I invoke a particular method on a generic type? I want to invoke a a getter/setter method.
Edit: Ex:
class BurningSun<T>
{
public void kitchenSink()
{
Class c = Class.forName(/*What to put here for <T> ?*/)
/*
Reflections code
*/
}
}
Hmmm, so how are the bean's getter methods invoked and set in various frameworks?
Inside of a generic type there is no way to get the name of the type parameter at runtime if nobody did tell it to you.
On runtime, a BurningSun<String> and BurningSun<Integer> are completely equivalent, and you can even cast one into the other (this is not type safe, though).
So, usually if you really need the class object of the type parameter inside your generic object, you let someone give it to you in the constructor.
class BurningSun<T> {
private Class<T> paramClass;
public BurningSun(Class<T> pClass) {
this.paramClass = pClass;
}
public void kitchenSink() {
T t = paramClass.newInstance();
}
}
(You would need to catch or declare some exceptions here.)
Well all method invocation based on reflection is at its most simple about shoving Objects into the method, praying it'll work. So cast & keep your fingers crossed.