class A<T> extends B<T> {}
class B<T> implements ParameterizedType {
#Override
public Type[] getActualTypeArguments() {
Type superclass = this.getClass().getGenericSuperclass();
System.out.println("A superClass:" + superclass);
return superclass.getClass().getTypeParameters();
}
#Override
public Type getRawType() {
return null;
}
#Override
public Type getOwnerType() {
return null;
}
}
public static void main(String[] args) {
Action<String> action = new Action<>();
action.getActualTypeArguments();
B<String> b = new B<>();
b.getActualTypeArguments();
}
the result is:
superClass:class java.lang.Object
superClass:Main$Action<T>
I want to get right result(java.lang.String), what can I do.
I Try to change this.getClass().getSuperClass().getGenericSuperclass(); in class B, but the result also is java.lang.Object...
B<String> b = new B<>();
It is impossible to get String out, that's what erasure means. No amount of futzing with .getActualTypeArguments() or getGenericType() let you do an end-run around this. If it was possible, don't you think there'd be considerably simpler APIs for this?
Bit for bit, in memory, there is no difference, whatsoever, between new B<String>() and new B<Integer>. Those generics are pretty much all things the compiler uses to link things together. It's the compiler that complains when you mess things up, java.exe has no idea what generics are.
Generics in signatures remain - they have to, as the compiler needs them to do its work, and the compiler often has only compiled code to work with (for example, to compile a hello world program, you need the java.lang.String class, but you don't need its source). As far as java.exe is concerned, these are comments - they exist solely for javac to look at it.
You can however, get at these with reflection, and that is what getGenericSuperclass() and all that jazz is about. So, while you cannot get the String out of B<String> b = new B<>(), you can get it out of the subtly different B<String> b = new B<>() {}. That's an anonymous inner class literal. If you don't know what they are, that's syntax sugar for:
// You can declare a class inside a method just fine
class $RandomName extends B<String> {
}
new $RandomName();
And, crucially, extends B<String>, that's signature stuff, and signatures can be read out with generics.
This principle is called a super type token. It means to pass along generics, you have to use this new Foo<TheActualGenericsYouWantToPass>() {} construct, ending in the braces, to ensure a new local anonymous class is made, as that then carries the generic type. The exact thing you put in it is then transfered, i.e. if you have:
public class Foo<T> {
public void example() {
new SuperTypeToken<T>() {};
}
}
You're creating one that is literally just T, and not whatever T actually is, in other words, if you then do: new Foo<String>.example(), that type token is still literally T, it cannot be used to derive String (again, erasure, it's literally impossible, that information just does not exist at runtime and therefore cannot possibly be derived).
You can search the web for tutorials on how to make these, and various libraries, such as JSON parsing libraries, have TypeTokens (and documentation on how to use them), which are all implementations of this idea.
Related
I would like to know if there is a way to do something like this in Java:
Integer a = new Integer(2);
a.getClass() newVariable = new Integer(4);
My question is can I declare the type of a new variable by using a variable?
It is not possible to specify the type by retrieving it from an other variable.
Note that the reflection tutorial of Oracle provide some methods that would simulate it. It will allow you to instantiate an object by specifying it type as a class. But that won't allow you to first specify the object type by retrieving it from the variable.
You might want to have a look at generics which would probably help you fix the hidden problem that made you ask this question.
A simple example would be
public class Foo<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
Note that T stands for Type as per the documentation.
#param the type of the value being boxed
So you can give any type.
You can also specify the extends of the type, which kind of give you some security.
class Foo<T extends Integer>
No, you cannot base the type of a variable on the type of another variable like that.
Depending on your real goal, you might be able to use Java's generics to get around your reason for feeling you want to do that, but it depends a lot on what your real end goal is. For instance, if this is all in class Foo, you could parameterize it, and then use the parameter in both places:
class Foo<T> {
public method doSomething() {
T a = /*...*/;
T newVariable = /*...*/;
}
}
Then:
Foo<Integer> f = new Foo<Integer>();
...results in an f with a doSomething that works with and returns Integers.
Note the /*...*/ after the = in the above: I can't do new T() there, which is a famous limitation of Java's generics. The values for a and newVariable would have to come from somewhere (arguments to the method, for instance, or data members, etc.).
More about generics here, again depending on whether they really help with what you're ultimately trying to do.
I'm playing with some functional like programming. And having issues with some pretty deeply nested generics. Here's my SCCE that fails, with an abstract class involved:
public abstract class FooGen<IN, OUT> {
OUT fn2(IN in1, IN in2) { // clever? try at a lazy way, just call the varargs version
return fnN(in1, in2);
}
abstract OUT fnN(IN...ins); // subclasses implement this
public static void main(String[] args) {
FooGen<Number, Number> foogen = new FooGen<Number, Number>() {
#Override Number fnN(Number... numbers) {
return numbers[0];
}
};
System.out.println(foogen.fn2(1.2, 3.4));
}
}
This dies with a
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Number;
However, for a non-abstract FooGen, it works fine:
public class FooGen<IN, OUT> {
OUT fn2(IN g1, IN g2) {
return fnN(g1, g2);
}
OUT fnN(IN...gs) {
return (OUT)gs[0];
}
public static void main(String[] args) {
FooGen<Number,Number> foogen = new FooGen<Number,Number>();
System.out.println(foogen.fn2(1.2, 3.4));
}
}
This prints 1.2. Ideas? It seems like somewhere Java has lost track of the generics. This is pushing the limits of my generics knowledge. :-)
(Added in response to answers)
First, thanks for the upvotes, and to Paul and Daemon for their helpful answers.
Still wondering why it works as Numbers in the 2nd version, I had an insight. As a Thought Experiment, let's add a .doubleValue() somewhere. You can't. In the code itself the variables are INs, not Numbers. And in the main() it's merely declaring the type, FooGen<Number,Number> but there's no place there to add code.
In Version #2, it really isn't "working" as Numbers. Internally, with erasure, everything is Objects, as explained by Paul and Daemon, and, looking back sheepishly, well understood by myself. Basically, in this complex example, I got overexcited and mislead by the <Number> declaration.
Don't think I'll bother with a workaround. The whole idea was to be lazy. :-) For efficiency I created parallel interfaces and code that take primitive doubles (and ints), and there this trick works just fine.
Varargs parameters are first and foremost arrays. So without the syntactic sugar, your code would look like the following:
OUT fn2(IN in1, IN in2) {
return fnN(new IN[] {in1, in2});
}
abstract OUT fnN(IN[] ins);
Except new IN[] would not be legal because arrays of type parameters cannot be instantiated, due to type erasure. An array needs to know its component type, but IN has been erased to its upper bound, Object, at runtime.
The varargs invocation hides this issue unfortunately, and at runtime you have the equivalent of fnN(new Object[] {in1, in2}), whereas fnN has been overriden to take a Number[].
However, for a non-abstract FooGen, it works fine
This is because by instantiating FooGen directly, you haven't overridden fnN. Thus it accepts an Object[] at runtime and no ClassCastException occurs.
For example, this will fail even if FooGen isn't abstract:
FooGen<Number, Number> foogen = new FooGen<Number, Number>() {
#Override
Number fnN(Number... gs) {
return super.fnN(gs);
}
};
System.out.println(foogen.fn2(1.2, 3.4));
So you can see that it really isn't related to the abstractness of FooGen, but to whether fnN gets overridden with a narrowed argument type.
SOLUTION
There are no easy workarounds. One idea is to have fnN take a List<? extends IN> instead:
OUT fn2(IN in1, IN in2) {
//safe because the array won't be exposed outside the list
#SuppressWarnings("unchecked")
final List<IN> ins = Arrays.asList(in1, in2);
return fnN(ins);
}
abstract OUT fnN(List<? extends IN> ins);
If you wanted to keep the varargs support, you could treat this method as an implementation detail and delegate to it:
abstract OUT fnNImpl(List<? extends IN> ins);
public final OUT fnN(IN... ins) {
return fnNImpl(Arrays.asList(ins));
}
This ClassCastException occurs due to a feature of Java called "type erasure". Type erasure occurs when generics are compiled. Since the Java compiler cannot know the type of a generic class at run-time, it will instead compile the generic objects as instances of Object.
In your code, when FooGen is compiled, fnN(IN... ins) receives a parameter of type Object[]. The ClassCastException occurs when you then attempt to down-cast one of these Objects to your generic type OUT.
This isn't even mentioning the fact that creation of such "generic arrays" is prohibited in Java regardless.
Here is a quote from Angelika Langer's Java Generics FAQ:
Here is another example that illustrates the potential danger of
ignoring the warning issued regarding array construction in
conjunction with variable argument lists.
Example (of a varargs method and its invocation):
public final class Test {
static <T> T[] method_1(T t1, T t2) {
return method_2(t1, t2); // unchecked warning
}
static <T> T[] method_2( T... args) {
return args;
}
public static void main(String... args) {
String[] strings = method_1("bad", "karma"); // ClassCastException
}
}
warning: [unchecked] unchecked generic array creation of type T[] for
varargs parameter
return method_2(t1, t2);
^
In this example the first method calls a second method and the second
method takes a variable argument list. In order to invoke the varargs
method the compiler creates an array and passes it to the method. In
this example the array to be created is an array of type T[] , that
is, an array whose component type is a type parameter. Creation of
such arrays is prohibited in Java and you would receive an error
message if you tried to create such an array yourself.
I know that what I present here is bad but still - I need to do this ...
I would like to check class of generic in given method. I tried using Guava and description from here: https://code.google.com/p/guava-libraries/wiki/ReflectionExplained#Introduction
This is something that I have and I don't fully understand why it doesn't work:
```
abstract static public class IKnowMyType<T> {
public TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
protected <P> void abc(P el){
System.out.println(new IKnowMyType<P>(){}.type);
}
protected <P> void abc(){
System.out.println(new IKnowMyType<P>(){}.type);
}
void test(){
System.out.println(new IKnowMyType<String>(){}.type); // -> java.lang.String
this.abc("AA"); // -> P
this.<String>abc(); // -> P
}
What I would like to get is proper class of P (String in this case) rather than P. How to do this? Why those abc methods doesn't work as I expect?
There is no way to do what you're trying to do, and this is working exactly as expected.
Type erasure destroys generic type information of objects at runtime, as well as knowledge of the type arguments of methods (like you're finding out here). What type erasure doesn't affect is that classes know their compile-time generic types, so e.g. if you have
class Foo<T> {}
class Bar extends Foo<String>
then Bar.class knows that it is a subclass of Foo<String>, not just Foo. That's how TypeToken works, but it only works when the type is fixed at compile-time; it can't be left as a type variable.
I have this class, just for the purpose of learning:
public class MyClass{ //Looking for a solution without making my class also generic <Type>
//Private Arraylist var to hold the value called myvar
public MyClass(ArrayList<MyDesiredType> incoming) {
//CODE myVar=incoming
}
public MyDesiredType getType() {
return myVar.get(0);
}
}
Is there any way to infer in the incoming object from the constructor to the return type of the method without warnings and castings and loosing typesafeness, but most of all WITHOUT making the whole class GENERIC (seems redundant to me)? If not, why should I think this is not feasible for the compiler?
This is a reformulated question I already did, but it was my first one and I learned how to expose it clear because nobody understood. I tried to edit later the original question but everything was buried. I changed and simplified the example and try to put it easy. Original question: Java Generics Silly Thing (Why cant I infer the type?).
If there is any problem just tell it to me and I will remove it.
No, there is not. How would the compiler know what type to return? The generic type of ArrayList in the constructor will not be known during compile time. You either have to make the whole class generic or take another approach.
Consider this:
public class Test {
public static void main(String[] args) {
List<String> arrList = new ArrayList<String>();
arrList.add("FOO");
Test test = new Test(arrList);
String testStr = test.returnWhat();
System.out.println("testStr");
}
private final List myList; //warning
public <T> Test(List<T> ttype) {
myList = ttype;
}
public <T> T returnWhat() {
return (T) myList.get(0); //warning
}
}
This works but gives you warnings on the marked lines. So, really there is no way to achieve what you are describing without making the whole class generic.
Because, what if:
public class Test {
public static void main(String[] args) {
List<String> arrList = new ArrayList<String>();
arrList.add("FOO");
Test test = new Test(); // now what?
String testStr = test.returnWhat(0); // no warning...
JPanel p = test.returnWhat(0); // goes through without warning, real nice...
test.returnWhat(0); // returns Object
Test test2 = new Test(arrList);
test2.addElement(new Object()); // boom, inserted object into list of string.
String nono = test2.returnWhat(1); // the universe goes down. assign an object to string without warning. even
// though one COULD think the class is generic.
}
// private List<T> myList = new ArrayList<T>(); compiler error, T is unknown
private List myList = new ArrayList();
public Test() {
myList.add(new Object());
}
public <T> Test(List<T> ttype) {
myList = ttype;
}
public <T> T returnWhat(int index) {
return (T) myList.get(index);
}
public <T> void addElement(T el) {
myList.add(el);
}
}
The second one doesn't compile when myList is made generic. How could the compiler determine the type of <T> in the case where the default constructor is used?
Further, this could lead to serious problems with Objects in collections that rely on the fact that only certain types are inserted.
This will generate the following exception:
Exception in thread "main" java.lang.ClassCastException:
java.lang.Object cannot be cast to java.lang.String at
Test.main(Test.java:27)
Did I manage to convince you?
Real nice question, btw. I had to think about this one quite a bit.
When you say that you want the compiler to "infer in the incoming object from the constructor to the return type of the method without warnings and castings and loosing typesafeness", it seems that you are saying that it should infer the result of getType() from the input of the constructor. If both happen in the same function, it could. The problem is that the object may not exist in only one function, and so the extra type information (the generic type) is needed to pass this kind of object between functions.
For example, if I want to write a function that takes a MyClass object, I need to know what getType() will return so I can use the returned value. By adding a generic type of MyClass we are giving a description to what it holds.
Another way to look at it is that MyClass is a container. By adding generics, we are saying it is a container of a specific type of thing, and so we can more easily predict what we will get out of it.
There is no way for the compiler to know at runtime what type your arraylist is. I really dont see the problem using something along the lines of this:
public class MyClass<TYPE> {
private ArrayList<TYPE> incoming;
public MyClass(ArrayList<TYPE> incoming) {
this.incoming = incoming;
}
public TYPE getType() {
return incoming.get(0);
}
}
This way you can do:
ArrayList<Integer> numbers = createListOfNumbers();
MyClass<Integer> myClass = new MyClass<>(numbers);
Integer number = myClass.getType();
Or am i misinterpreting the question and you want to know the class at runtime?
No, if you want a class that can hold a list of a parameterized type.
Yes, if you want a class that can hold a list of exactly one type. You can declare that type explicitly in the field, constructor and accessor.
What you're forgetting is that not all code that you may run against is visible to the compiler! Jars can be added, removed, substituted at run time, that the compiler never saw. You may compile against an interface that is just:
public interface MyClassFactory {
MyClass getInstance();
}
Then at runtime you supply into the JVM an implementation. So the compiler never saw the actual code creating the MyClass that you will be using, so there is no way to perform such a compile time inference. You must either make the class generic or accept that there will not be type safety.
we have been simplifying some definition and usage of generics in our code.
Now we got an interesting case, take this example:
public class MyWeirdClass {
public void entryPoint() {
doSomethingWeird();
}
#SuppressWarnings( "unchecked" )
private <T extends A & B> T getMyClass() {
if ( System.currentTimeMillis() % 2 == 0 ) {
return (T) new MyClass_1();
} else {
return (T) new MyClass_2();
}
}
private <T extends A & B> void doSomethingWeird() {
T obj = getMyClass();
obj.methodFromA();
obj.methodFromB();
}
static interface A {
void methodFromA();
}
static interface B {
void methodFromB();
}
static class MyClass_1 implements A, B {
public void methodFromA() {};
public void methodFromB() {};
}
static class MyClass_2 implements A, B {
public void methodFromA() {};
public void methodFromB() {};
}
}
Now look at the method 'doSeomthingWeird() in MyWeirdClass:
This code will compile correctly using the eclipse JDT compiler, however it will fail when using the Oracle compiler. Since the JDT is able to produce working byte-code, it means that at JVM level, this is valid code and it is 'only' the Oracle compiler not allowing to compile such dirty(!?) stuff.
We understand that Oracle's compiler won't accept the call 'T obj = getMyClass();' since T is not a really existent type. However since we know that the returned object implements A and B, why not allowing it? (The JDT compiler and the JVM do).
Note also that since the generics code is used only internally in private methods, we do not want to expose them at class level, polluting external code with generics definitions, that we are not interested at (from outside the class).
The school book solution will be to create an interface AB extends A,B however since we have a larger number of interfaces which are used in different combinations and coming from different modules, making shared interfaces for all the combinations will significantly increase the number of 'dummy' interfaces and finally make the code less readable. In theory it would require up to N-permutations of different wrapper interfaces in order to cover all the cases.
The 'business-oriented-engineer'(other people call it the 'lazy-engineer') solution would be to leave the code this way and start using only JDT for compiling the code.
Edit: It's a bug in Oracle's Javac 6 and works without problems also on Oracle's Javac 7
What do you mean? Are there any hidden dangers by adopting this 'strategy'?
Addition in order to avoid discussion on (for me) not relevant points:
I am not asking why the code above does not compile on Oracle's compiler I know the reason and I do not want to modify this kind of code without a very good reason if it works perfectly when using another compiler.
Please concentrate on the definition and usage (without giving a specific type) of the method 'doSomethingWeird()'.
Is there a good reason, why we should not use only the JDT compiler that allows writing and compiling this code and stop compiling with the Oracle's compiler, which will not accept the code above?
(Thanks for input)
Edit: The code above compiles correctly on Oracle Javac 7 but not on Javac 6. It is a Javac 6 bug. So this means that there is nothing wrong in our code and we can stick on it.
Question is answered, and I'll mark it as such after the two days timeout on my own answer.
Thanks everybody for the constructive feedback.
In java you can do generic methods if generic type used in Parameter or Return Type of method signature. In your sample generic doSomethingWeird method but never used it in method signature.
see following sample:
class MyWeirdClass
{
public void entryPoint()
{
doSomethingWeird(new MyClass_1());
}
private <T extends A & B> T getMyClass()
{
if (System.currentTimeMillis() % 2 == 0)
{
return (T) new MyClass_1();
}
else
{
return (T) new MyClass_2();
}
}
private <T extends A & B> void doSomethingWeird(T a)
{
T obj = getMyClass();
obj.methodFromA();
obj.methodFromB();
}
}
This code work fine.
JLS(Java Language Specification) says in Generic Method part:
Type parameters of generic methods need not be provided explicitly when a
generic method is invoked. Instead, they are almost always inferred as specified in
ยง15.12.2.7
By this quotation when you don't use T in doSomethingWeird method signature,What you specify raw type of T in invoking time(in entryPoint method)?
I did not check the code (compile with both compilers). There is a lot of weird stuff in language specification at even more basic level (well, check the array declaration...). However, I believe that the design above is little "over-engineered" and if I translate the need correctly, the required functionality can be achieved with Factory pattern or if you are using some IoC framework (Spring?) then lookup method injection can do the magic for you. I think the code will be more intuitive and easy to read and to maintain.
I think the cause is different. That's not true that the type T on line "T obj = getMyClass();" is unknow - in fact, because of definition "T extends A & B", its erasure is A. This is called multiple bounds and following applies: "When a multiple bound is used, the first type mentioned in the bound is used as the erasure of the type variable."
Based on #MJM's answer, I suggest you to update the code as below. Then your code will not rely on JVM's type infer.
public void entryPoint() {
doSomethingWeird(getMyClass());
}
private <T extends A & B> T getMyClass() {
if (System.currentTimeMillis() % 2 == 0) {
return (T)new MyClass_1();
} else {
return (T)new MyClass_2();
}
}
private <T extends A & B> void doSomethingWeird(T t) {
t.methodFromA();
t.methodFromB();
}
I would go again for creating what you called a "dummy" interface AB.
First of all, I don't find it dummy at all. There are two classes with the same common method definitions and at one place you need to use one of them regardless of which one it is actually. That is the exact useage of inheritence. So the interface AB fits perfect here. And generics is the wrong solution here. Generics were never meant to implement inheritance.
Second, defining the interface will remove all the generics stuff in your code, and will make it much more readable. Actually adding an interface (or class) never makes your code less readable. Otherwise, it would be better to put all the code in a single class.
This is what the OpenJDK guys answered to my question:
These failures are caused by the fact that JDK 6 compiler doesn't
implement type-inference correctly. A lot of effort has been put into
JDK 7 compiler in order to get rid of all these problems (your program
compiles fine in JDK 7). However, some of those inference improvements
require source incompatible changes, which is why we cannot backport
these fixes in the JDK 6 release.
So this means for us: There is absolutely nothing wrong with our code and is officially supported also by Oracle. We can also stick to this kind of code and use Javac 7 with target=1.6 for our maven builds while development in eclipse will guarantee that we do not use Java 7 APIs :D yaaahyyy!!!
Your approach is questionable, because the unchecked casts sacrifice runtime type safety. Consider this example:
interface A {
void methodFromA();
}
interface B {
void methodFromB();
}
class C implements A { // but not B!
#Override public void methodFromA() {
// do something
}
}
class D implements A, B {
#Override
public void methodFromA() {
// TODO implement
}
#Override
public void methodFromB() {
// do something
}
}
class Factory {
#SuppressWarnings( "unchecked" )
public static <T extends A & B> T getMyClass() {
if ( System.currentTimeMillis() % 2 == 0 ) {
return (T) new C();
} else {
return (T) new D();
}
}
}
public class Innocent {
public static <T extends A & B> void main(String[] args) {
T t = Factory.getMyClass();
// Sometimes this line throws a ClassCastException
// really weird, there isn't even a cast here!
// The maintenance programmer
t.methodFromB();
}
}
(You may have to run the program several times to see what confused the maintenance programmer.)
Yes, in this simple program the error is rather obvious, but what if the object is passed around half your program until its interface is missed? How would you find out where the bad object came from?
If that didn't convince you, what about this:
class NotQuiteInnocent {
public static void main(String[] args) {
// Sometimes this line throws a ClassCastException
D d = Factory.getMyClass();
}
}
Is eliminating a couple interface declarations really worth that?