Java Generics (simple case, clear enigma for infering) - java

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.

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
}
}

Java Collection Type Parameter to array

I want to create a helper method which gets Collection type parameter to return a list. This is what I have now:
public class Helper {
public static <T> T[] CollectionToArray(Collection<T> collection) {
return collection.stream().toArray(Object[]::new); // Error
}
public static <T> T[] ListToArray(List<T> list) {
return list.stream().toArray(Object[]::new); // Error
}
}
public class IamSoNoob {
public void PleaseHelpMe() {
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
String[] arrayFromList = Helper.CollectionToArray(list); // Error
String[] arrayFromSet = Helper.CollectionToArray(set); // Error
String[] array = Helper.ListToArray(list); // Error
}
}
My questions are:
Is it possible to complete CollectionToArray(Collection<T>)?
If so, how?
Also, is it possible to pass List and Set as a parameter in the first place?
Is it possible to complete ListToArray(List<T> list)?
If so, how?
But here are some restrictions due to my personal taste.
I don't want to use #SuppressWarnings
I really want to keep the part .stream().toArray(Object[]::new) (Java 8 part!)
And I have a feeling that I need to fix the part Object[]::new by using something like: <T extends Object> or <? extends T> but I can't really figure out.
Please help me out, and please provide an explanation as well, I am often confused by Generic and ?.
No, you absolutely cannot do it, if it were possible the library method Collection.toArray() would've given you the same type as your LHS but instead when you want the exact type as your LHS you have to use Collection.toArray(T[]) (even that comes with ArrayStoreExceptions i.e it is up to the programmer to provide the right type for the array), the reason being that in your toArray() you've specified Object[] to be your array and later you cannot cast it to any other type or else it will result in a ClassCastException.
The reason for all this hullabaloo is because of the way generics works i.e its a compile time thing and at runtime Java erases all type parameters to their upper bound types and hence losing type information which is required for creating arrays.
One safe way of doing it is by adding another paramter to you helper method as
public static <T> T[] CollectionToArray(Collection<T> collection, T[] arr) {
return collection.stream().toArray(value ->Arrays.copyOf(arr,collection.size()));
}
and using it as
String[] arrayFromList = Helper.CollectionToArray(list, new String[0]);
but then everybody's better off using
Collection#toArray(T[]).

How to initialize a generic array containing generics?

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...
}
}

Without specifying a generic type, method does not return a list of elements, but simply a list

I am having a slight inconvenience when working with generics in Java. Please consider the following code:
/**
* MyElement class is simply a wrapper for a generic object.
*/
public static class MyElement<T> {
public final T OBJ;
public MyElement(T obj) {
this.OBJ = obj;
}
}
/**
* MyElementList contains an array list of MyElements of the given type, T.
* This represents a class that uses a list of MyElements of a certain type,
* and this list can be accessed in an unmodifiable format.
*/
public static class MyElementList<T> {
//Properties
private List<MyElement<T>> elementList = new ArrayList();
//CTOR
public MyElementList(List<MyElement<T>> initElements) {
elementList.addAll(initElements);
}
//Getter
public List<MyElement<T>> getElements() {
return Collections.unmodifiableList(elementList);
}
}
public static void main(String[] args) {
//New list of elements
//Notice that I did not explicitly specify the type for 'MyElement'
List<MyElement> theElements = new ArrayList(Arrays.asList(
new MyElement[] {
new MyElement("E 1"),
new MyElement("E 2"),
new MyElement("E 3")
}
));
//Also notice I did not explicitly specify the type for 'MyElementList'
MyElementList theList = new MyElementList(theElements);
//The following does not work.
//It seems to not work because theList.getElements() returns a 'List'
//not necessarily a 'List<MyElement>' which is what I would expect it to
//return...
//Why???
for(MyElement e : theList.getElements()) {
System.out.println(e.OBJ.toString());
}
//Currently my work around is to do the following, but I do not like
//having to introduce another variable, and I would rather just do the
//one above
List<MyElement> listOfElements = theList.getElements();
for(MyElement e : listOfElements) {
System.out.println(e.OBJ.toString());
}
//How come the first 'for-each' loop method does not work?
//Is there anyway I could get it to work?
//THANK YOU!
}
In the main method, if I don't specify the type parameter for 'MyElementList' the 'getElements()' method only returns a 'List', not a 'List<MyElement>'. This is inconvenient because if I want to iterate through each 'MyElement' I need to introduce another variable as a temporary list, shown in the code.
Why doesn't the 'getElements()' method return a 'List<MyElement>'?
Without making significant changes to 'MyElementList' Is there anything I can do to fix this?
Is this a bad design practice?
The IDE I am using is Netbeans 7.2
Thanks in advance!
EDIT
Thank you all for your quick responses. I am very impressed with the community here. I have concluded the following:
If a generic hint is not specified, Java ignores ALL other associated generic hints for a class - which is kind of lame, but I can live with it.
When using generics, it is a best practice to actually specify the generic type when creating an instance of the class. This seems to be the most object oriented solution.
Thanks again!
If you change MyElementList to look like
public static class MyElementList<T extends MyElement> {
//Properties
private List<T> elementList = new ArrayList<T>();
//CTOR
public MyElementList(List<T> initElements) {
elementList.addAll(initElements);
}
//Getter
public List<T> getElements() {
return Collections.unmodifiableList(elementList);
}
}
It should work.
EDIT Generics can be seen as compile time hints in Java, since Java erasure will convert generics to Object. Updating your class as above will tell the compiler only elements which extend MyElement fit the list and for(MyElement e : theList.getElements()) will work.
EDIT 2 As pointed out by others (sorry, I didn't see it at first glance) also change the raw declaration to:
MyElementList<MyElement> theList = new MyElementList<MyElement>(theElements);
Te first does not work because getElements returns a List<?> for the raw type
The second works because you assigned it to a List<MyElement>, ignoring the warning. Ignoring was ok because you know what it contains, but the compiler doesn't.
Instead of using
for(MyElement e : theList.getElements()) {
System.out.println(e.OBJ.toString());
}
you could use
for (Iterator<MyElement> it = theList.getElements().iterator(); it.hasNext();) {
MyElement e = it.next();
System.out.println(e.next().OBJ.toString());
}
which makes your compiler compliant.
But I would prefer to specify the types that your classes require when instantiating/accessing them (and your compiler too, I guess ;)).
Why doesn't the getElements() method return a List<MyElement>
Because MyElement is typed!
Without making significant changes to MyElementList Is there
anything I can do to fix this?
You can probably use a wildcard:
List<MyElement<?>> someList = getElements();

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