How does parameterization work in Collections.EmptyList class? - java

I just wonder what happens after I execute:
List list = Collections.<String>emptyList();
Here is Collections code:
public static final List EMPTY_LIST = new Collections.EmptyList(null);
public static final <T> List<T> emptyList() {
return EMPTY_LIST;
}
private static class EmptyList<E> extends AbstractList<E>
implements RandomAccess, Serializable {
private EmptyList() {
}
//...
}
How is it possible to call EmptyList(null) while EmptyList doesn't have constructor with arguments.
Please, explain me the process how does Collection's method generic type T becomes EmptyList's generic type E?

I just wonder what happens after I execute
Nothing happens because everything about Generics and type parameters goes on at compile time.
How is it possible to call EmptyList(null) while EmptyList doesn't have constructor with arguments
It is not possible, but no JDK code I saw creates the empty list by passing null to the constructor. Don't trust decompiler code.
explain me the process how does Collection's method generic type T becomes EmptyList's generic type E?
T is a type variable capturing the type inferred at each call site of emptyList(). This variable is "passed" (during static type analysis) to emptyList so that the type of the returned value is List<T>. E is the name of the type parameter of EmptyList and this type parameter assumes the value of T in that particular type instantiation. This is very similar to how an ArrayList<E>'s E becomes String when you say new ArrayList<String>().

In addition to #MarkoTopolnik answer I can explain why decompiler shows that null is passed into constructor parameters.
When you create private class with no explicit constructor, its default no-arg constructor is generated inside the bytecode as private:
private static class EmptyList<E> {
private EmptyList() {
}
...
}
This way the class cannot be instantiated from other classes (JVM will not permit this). But it does instantiated in Collections class:
public static final List EMPTY_LIST = new EmptyList<>();
From the JVM point of view there's no thing like "nested class". The EmptyList class is just another class (named Collections$EmptyList) in the same package. If javac compiler generate the direct call to the private constructor, then JVM will just throw an exception during the Collections class initialization. To fix this problem javac compiler introduces one more additional constructor with package-private access:
private static class EmptyList<E> {
private EmptyList() {
}
EmptyList(Collections$1 ignore) {
this();
}
...
}
This artificial constructor has one parameter just to distinguish from the existing constructor. Also to avoid possible arguments clash an additional artificial class Collections$1 is generated! The purpose of this class is just to be the parameter of such artificial constructors. It's never instantiated and even initialized.
So finally you can instantiate the EmptyList from another class (within java.util package) calling this new constructor:
public static final List EMPTY_LIST = new EmptyList<>((Collections$1)null);
This is how it looks in the bytecode. Seems that decompiler is not smart enough to detect this situation and remove the artificial constructor parameter in the output (actually it should be quite simple).

Your decompiled code:
public static final List EMPTY_LIST = new Collections.EmptyList(null);
Actual code
#SuppressWarnings("unchecked")
public static final List EMPTY_LIST = new EmptyList<>();
In the actual code, there is no parameter to the constructor.
Your decompiled code:
public static final <T> List<T> emptyList() {
return EMPTY_LIST;
}
Actual code:
#SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
So, as for how does the type T becomes E? By cheating. EMPTY_LIST is a raw type, and they explicitly cast it into List<T> and suppress the warnings both on creation of the original EMPTY_LIST and on this explicit cast.
In reality, this is pretty safe exactly because the list is empty and will stay empty.
Moral of the story: don't trust decompilers, especially not when it comes to generics, because with type erasure, it's really hard for a decompiler to know what actually happened in the original source code. If you want to look at the source of various open-source Java projects, including OpenJDK, use the useful (though slow) GrepCode website.

Related

Discrepancy in Generic parametric polymorphism

public class Foo<T extends Bar>{
private Class<T> _type;
public Foo( Class<T> _type ){
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ){ //PROBLEMATIC
return dostuffWithItems( items );
}
}
Usage:
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
List<ChildBar> items = ( List<ChildBar> ) foo.hypothetical( new ArrayList<ChildBar>() ); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>)
The compiler would either accept
casting List<ChildBar> items argument to List<?>
or changing the hypothetical( List<T> items ) signature to either
a) hypothetical( List<ChildBar> items ) or
b) hypothetical( List<? extends Bar> items )
However, none of the alternatives assure that the hypothetical method's List items argument T type is the equivalent runtime type of the Foo class T parametric type. I am currently using an extra method to verify the parametric types at the moment.
Is there a better way within Java generics constructs to achieve this automatically without the extra logic? Or better yet, why can I not declare foo as Foo<? extends Bar> and then fill in the actual type parameter at runtime?
I edited your code and added the missing stuff to make it compilable, and I can confirm that the only problematic parts are:
The missing dostuffWithItems method.
The typos with the hypothetical method name.
Assigning a Collection<ChildBar> to a List<ChildBar>.
The first two are easy to fix.
The last one requires you to either change the change the API method, or change the code where you are calling it. Neither of these is (IMO) problematic. Furthermore, the
It is worth noting that you would get all of these errors if the types were non-generic. You can't assign a Collection to a List without a typecast.
Here's my code for you to play with. (Copy and paste into appropriately named files ...)
public class Bar {
}
public class ChildBar extends Bar {
}
import java.util.*;
public class Foo<T extends Bar> {
private Class<T> _type;
public Foo( Class<T> _type ) {
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ) {
return items; // dummy implementation ...
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
Collection<ChildBar> items =
foo.hypothetical( new ArrayList<ChildBar>() );
}
}
The accepted answer doesn't precisely explain why the snippet in the question (after edits) is rejected by the compiler.
We start from the observation that the snippet from #Stephen C's answer is accepted, while revision 8 of the question is rejected. The difference is: in the latter version the variable foo is declared with a wildcard-parameterized type Foo<? extends ChildBar>, while Stephen C had copied Foo<ChildBar> from an earlier revision (we all seem to agree that this is a suitable way to resolve the compile error).
To understand why this difference is crucial please see that with Foo<? extends ChildBar> foo this wildcard propagates as a capture into the signature for the invocation of foo.hypothetical, so this invocation is rendered as hypothetical(List<capture#2-of ?>), meaning that the parameter has an unknown (upper-bounded) type parameter. List<ChildBar> is not compatible to that type, hence the compile error.
Also note that all mentions of "runtime" in this thread are inappropriate, all this is statically resolved at compile time. Perhaps you meant invocation type, or type of the actual argument, as opposed to the declared type (of the formal parameter). The actual runtime type is unknown to the compiler.
This seems to be currently impossible in Java.
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
This leave foo with an ambiguous parametric type. It is obvious that ChildBar would become the true de facto parametric type. The call to the foo.hypothetical() method with the List<ChildBar> exposes this assumption to be untrue. Although foo.hypothetical only accepts a List<> argument containing elements of the foo parametric type, it still fails to recognize that the argument was a list of ChildBar objects.
For this use case, the object parametric type must be specified during foo declaration in order make it a part and parcel of the foo runtime reference.
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
All conforming List<ChildBar> arguments of the foo.hypothetical method will now correctly be accepted as carrying elements of the foo's declared parametric type.

Java Generics (simple case, clear enigma for infering)

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.

Get type name for generic parameter of generic class [duplicate]

This question already has answers here:
Get generic type of class at runtime
(30 answers)
Closed 4 years ago.
I have a small problem in java while using genericity. I have a class A :
public class A<T>
In a method of A, I need to get the type name of T.
Is there a way to find the string s using T ?
(If I create A<String> temp = new A<String>();, I want to be able to get java.lang.String at one point - I have to use genericity because one of my methods will have to return a List<T>).
This seems quite easy but I do not see how to do it.
You can't do this in general because of type erasure - an instance of A<String> doesn't know the type of T. If you need it, one way is to use a type literal:
public class A<T>
{
private final Class<T> clazz;
public A<T>(Class<T> clazz)
{
this.clazz = clazz;
}
// Use clazz in here
}
Then:
A<String> x = new A<String>(String.class);
It's ugly, but that's what type erasure does :(
An alternative is to use something like Guice's TypeLiteral. This works because the type argument used to specify a superclass isn't erased. So you can do:
A<String> a = new A<String>() {};
a now refers to a subclass of A<String>, so by getting a.getClass().getSuperClass() you can eventually get back to String. It's pretty horrible though.
You can get the name of the generics from the subclass. See this example.
We Define a parent class like this:
public class GetTypeParent<T> {
protected String getGenericName()
{
return ((Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]).getTypeName();
}
}
We then define its child class in this way:
public class GetTypeChild extends GetTypeParent<Integer> {
public static void main(String[] args) {
GetTypeChild getTypeChild = new GetTypeChild();
System.out.println(getTypeChild.getGenericName());
}
}
You can see that in the main method, or in any instance method, I am capable to get the name of the generics type, in this case the main will print: java.lang.Integer.
Short answer: Impossible.
Slightly longer answer: Once your code is compiled, the type parameters is discarded.
Thus, Java cannot know what you set there.
You could, however, pass the class in question to your object and operate on it:
public class Example<T> {
private final Class<T> clazz;
public Example(Class<T> clazz){
this.clazz = clazz;
}
...
}
As is normally the case, Apache has a solution for this one with TypeUtils:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/TypeUtils.html
A quick example from the above question:
TypeUtils.getTypeArguments(temp.getClass(), A.class).get(A.class.getTypeParameters()[0])
Disclaimer: I did not attempt building this first, but have used this utility in a similar fashion in the past.
Generics in Java are implemented by erasure, so no, you won't be able to get the name of the "type" which was used to create your generic collection at run-time. Also, why not just inspect the elements to know what type it belongs to?
If you're doing it in a subclass which has it's parent class defining the generic type, this is what worked for me:
// get generic type class name
String name = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].toString();
// then when you've got the name, you can make the Class<T> object
Class.forName(name.replace("class ", ""))
Reason why I couldn't do it with #getClass() instead of #toString() in the first snip is that I was always getting the "Class" class, which is useless to me.

Java Generics: Why does an explicit cast cause a compiler error, but variable assignment does not

This block compiles properly:
ArrayList<Baz> list = savedInstanceState.getParcelableArrayList("foo");
bar(list);
But this block errors stating that ArrayList<Parcelable> can not be cast to ArrayList<Baz>:
bar((ArrayList<Baz>)savedInstanceState.getParcelableArrayList("foo"))
Where bar is of the form:
private void bar(ArrayList<Baz> food) {
}
And Baz is a class that implements the Parcelable interface
Is there a way that the direct cast can be done rather than having to perform an implicit cast and create an unnecessary variable?
In order to use reference the method bar(ArrayList<T> food), you must perform a generic type invocation, which replaces T with some concrete value. T must be bounded to some type else, introduce wildcards like bar(ArrayList<?> food).
Reference.
Both those blocks are the same. Take this as an example which compiles:
import java.util.ArrayList;
public class Test<T> {
public void test(){
ArrayList<T> list = (ArrayList<T>)foo();
bar(list);
bar((ArrayList<T>)foo());
}
private ArrayList<Integer> foo(){ return null; }
private void bar(ArrayList<T> food) {}
}
I got the same problem, and my solution is to create a method to convert Parcelable to Baz.
For example..
private ArrayList<Baz> convertParcelableToBaz(ArrayList<Parcelable> parcelableList){
ArrayList<Baz> bazList= new ArrayList<Baz>();
for (int i = 0 ; i < parcelableList.size(); i++){
bazList.add((Baz)parcelableList.get(i));
}
return bazList;
}
so that
ArrayList<Baz> list = convertParcelableToBaz(savedInstanceState.getParcelableArrayList("foo"));
and no unchecked cast warning.
What is the error that you're getting; I would think that you'll only get a warning stating that it is an unchecked cast. If this is the case, you will need to add #SuppressWarnings("unchecked") to the function that you're FROM. To be honest; you'd be best to create a separate variable to catch the return value then send the variable.
One of the issues that you'll see here is that Java's type erasure is going to hurt you. If T is declared differently between calling function and receiving function, when T is removed there is nothing that prevents someone from actually sending your function which expects ArrayList an ArrayList. Which is why this is a type safety issue.

Type-safe, generic, empty Collections with static generics

I return empty collections vs. null whenever possible. I switch between two methods for doing so using java.util.Collections:
return Collections.EMPTY_LIST;
return Collections.emptyList();
where emptyList() is supposed to be type-safe. But I recently discovered:
return Collections.<ComplexObject> emptyList();
return Collections.<ComplexObject> singletonList(new ComplexObject());
etc.
I see this method in Eclipse Package Explorer:
<clinit> () : void
but I don't see how this is done in the source code (1.5). How is this magic tomfoolerie happening!!
EDIT:
How is the static Generic type accomplished?
return Collections.<ComplexObject> emptyList();
Using this will get rid of warnings from Eclipse about non-generic collections.
Having said that, a typed empty list is going to be functionally identical to an untyped empty list due to empty list being immutable and Java erasing generic types at compile time.
EDIT: How is the static Generic type
accomplished?
http://www.docjar.com/html/api/java/util/Collections.java.html
public class Collections {
...
public static final List EMPTY_LIST = new EmptyList<Object>();
...
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
...
}
You can see the link for the implementation of the EmptyList class if you're curious, but for your question, it doesn't matter.
<clinit> is the static initializer block. It is a block of code which is executed exactly once (when the class is loaded).
So, instead of writing
class A {
static int x = 5;
}
One can write:
class A {
static int x;
static { // static initializer starts
x = 5;
}
}
These two classes are equivalent. Inside a static initializer block one can place arbitrary code and thus initialize static fields with the results of complicated calculations.
<clinit> is the name of the method into which the class initialization code is collected by during compilation. (That is, all of the code inside static {} blocks, and the initializers of static members, in source code order.)
It has nothing to do with explicit type parameters in method invocations.

Categories

Resources