How to initialize a generic array containing generics? - java

I have the following code:
public class I<T> {
private T t;
public I(T t) {
this.t=t;
}
}
public class G<T> {
private I<T> tab[];
public G() {
tab=(I<T>[]) new Object[10];
}
}
Calling G() throws a ClassCastException.
How could I code the G constructor in order to initialize tab?

tab=(I<T>[]) new I<?>[10];
is the answer, but it is still mysterious for me!

To hopefully demystify your own answer a bit: be aware, that java implements generics via erasure, i.e. the compiler basically does some compile time checks, and then discards the generics.
So, from the runtime point of view, your first approach boils down to:
I[] tab = (I[]) new Object[10];
As array types are really distinguished classes, you get a class cast exception here, as I[].class is not the same as Object[].class.
Your second approach is (generics discarded):
I[] tab = (I[]) new I[10];
... no problems here.

Tab is an array of I. I is an Object, but a simple Object created by new Object() is not a I. That explains why the first way produced an error. You must create a array of I to initialize tab.
But that's not all: java generics use type erasure. That means that at run time the compiler does not know what T is. tab = new I<T>[10]; is executed at run time when T has no meaning any more, hence the error.
The solution is to create an array of I of any (new I<?>) or an array of I<Object> or even use the old (pre-generic) syntax new I[10].
That's not asked here, but when you really need to know the type of the parameter in a generic, the common idiom is to pass explicitely the class in constructor or with a setter so that the generic object can use it. Code could be:
class G<T> {
private I<T> tab[];
private Class<T> clazz;
...
public G(Class<T> clazz) {
this.clazz = clazz;
tab = new I[10];
// here we can use the type of T from clazz by reflexion...
}
}

Related

Type erasure Generics

An error occurs at new T[5] during compile-time saying => error: generic array creation
and according to my understanding, the array is created during compile-time and since we don't know the type of T at compile-time we cannot instantiate an array.
But
if T gets erased at compile-time and changes to Object then still why this error occurs ? because we can create an array of Object.
// Before Compiling
public class GenericClass<T> {
GenericClass(){
T[] obj = new T[5];
}
}
// After Compiling
public class GenericClass {
GenericClass() {
Object[] obj = new Object[5];
}
}
Similar case, like,
public class GenericClass<T> {
GenericClass(){
T obj = new T(); }}
/* error :required: class
found: type parameter T
where T is a type-variable:
T extends Object declared in class GenericClass*/
according to my understanding, the array is created during compile-time
No, the array is created at runtime.
nd since we don't know the type of T at compile-time we cannot instantiate an array.
Correct.
But if T gets erased at compile-time and changes to Object then still why this error occurs ?
Because "it is erased at compile time and changes to Object" is oversimplified.
Also, generics and arrays don't play nice with each other. The problem is, where the generics part is erased, arrays do not work like that. You can do this:
String[] x = new String[10];
tellMeTheTypeOfMyArray(x);
void tellMeTheTypeOfMyArray(Object[] o) {
System.out.println("Your array's component type is: " + o.getClass().getComponentType());
}
This code will compile and work fine, without error, and prints:
Your array's component type is: java.lang.String
Contrast to generics where you cannot write such a method. You cannot possibly make this:
List<String> x = new ArrayList<String>();
tellMeTheTypeOfMyList(x);
void tellMeTheTypeOfMyList(List<?> o) {
System.out.println("Your list's component type is: " + ??????);
}
work. There's no java code possible here, nothing you can write in place of the ?????? to print String, because that information simply is not there at runtime anymore.
Imagine this code:
// This is written by Wahab today.
class Example<T> {
protected T[] arr;
Example() {
this.arr = new T[10];
}
}
and it worked like you wanted. Then I do:
// Written by me, a year later
class Uhoh extends Example<String> {
Uhoh() {
super();
}
void hmmm() {
System.out.println(this.arr.getComponentType());
}
}
I would obviously expect, nay, demand - that this prints java.lang.String, but it could not possibly do so. Because this is weird and confusing, java has a rule: If you compile your code and you do not see any warnings about generics problems (and did not #SuppressWarnings them away), then this kind of confusion is not likely to happen.
Allowing you to write new T[] and having that just be a silly way to write new Object[] is considered too far gone for this.
So how do I use arrays with generics types?
The same way java.util.ArrayList does it: Do not use generics here. Arrays should pretty much never have T types if you intend to create them inside the generic code. If you have a T[] anywhere in your codebase, then that means you should never be new-ing up anything for it - let the caller of your code do it for you. If you do want to new up new arrays yourself, don't use T, use Object[] as type, and cast to T where needed. This is literally how java's built-in ArrayList class works. Some excerpts copy-pasted straight from its source:
transient Object[] elementData; // non-private to simplify nested class access
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
#SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
Here's an example, again straight from ArrayList's sources (or rather, java.util.Collection defines this, and ArrayList inherits it), where you let the caller provide you with code to make arrays:
default <T> T[] toArray(IntFunction<T[]> generator) {
return toArray(generator.apply(0));
}
Here the caller provides a function that transforms an int into a T[] - it takes the concept of doing new String[10] and turns it into a function, that you then pass along to the toArray method which will then use it (feel free to ignore how it uses it here, it's a bit of a bizarre solution. It works, just - not sure you should be learning lessons about that part).
You use it like this:
List<String> listOfStrings = ...;
String[] convertedToArray = listOfStrings.toArray(String[]::new);
Java arrays know their component type at runtime. When you create an array, you must provide the component type at runtime. But in your GenericClass, it cannot do that because it does not know what T is at runtime. If it creates an Object[], that object will have the wrong runtime class, and that instance is not compatible with the type T[] if T is anything other than Object. You are correct that, within the class, nothing is immediately wrong. But if the claim that the variable is T[] is exposed to an outside scope which expects T to be a more specific type, it can cause a ClassCastException:
// Before type erasure
class GenericClass<T> {
T[] obj;
GenericClass() {
obj = new T[5]; // if hypothetically you could do this
}
T[] getObj() {
return obj;
}
}
class MyCode {
public static void main(String[] args) {
GenericClass<String> foo = new GenericClass<>();
String[] strings = foo.getObj(); // no casts needed; no warnings
}
}
// After type erasure
class GenericClass {
Object[] obj;
GenericClass() {
obj = new Object[5];
}
Object[] getObj() {
return obj;
}
}
class MyCode {
public static void main(String[] args) {
GenericClass foo = new GenericClass();
String[] strings = (String[]) foo.getObj(); // ClassCastException at runtime
}
}

Declare new class from a Variable in Java

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.

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.

Categories

Resources