How Generics at Class Level Parameter works [duplicate] - java

This question already has answers here:
Java generics type erasure of method parameters
(3 answers)
Closed 9 years ago.
Consider Following code from Java Puzzlers
class Gloam<T>{
String glom(Collection<?> objs ) {
System.out.println("collection");
String result = "";
for (Object o : objs ){
result += o;
}
return result;
}
int glom(List <Integer> ints ) {
System.out.println("List");
int result = 0;
for ( int i : ints )
result += i ;
return result;
}
public static void main(String[] args) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam().glom(strings));
}
}
When I run this program it gives class cast exception, But if I provide any Generic argument for Gloam class in main method it works fine.
public static void main(String[] args) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam<Date>().glom(strings));
}
I don't understand how generic works in class type parameter ?

With no generic type passed into constructor, all types are erased and the compiler is presented with this choices
String glom ( Collection );
int glom ( List );
The type is also erased from strings variable defined in main, so its type is List.
Because List is more specific than Collection it chooses int glom ( List ).
Now, if you have specified the generic parameter, then no type erasure happens, and compiler knows that it cannot match int glom ( List<Integer> ) to List<String>, so it falls back to String glom ( Collection<?> )

Once you fail to provide the generic type parameter, all generic typing for the whole class is gone in the eyes of the compiler. The class essentially becomes:
class Gloam<T> {
String glom(Collection objs) {
System.out.println("collection");
String result = "";
for (Object o : objs) {
result += o;
}
return result;
}
int glom(List ints) {
System.out.println("List");
int result = 0;
for (int i : ints)
result += i;
return result;
}
public static void main(String[] args) {
List strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam().glom(strings));
}
}
So now the compiler will choose the int glom(List ints) override since that's the most-specific override that matches the call. But it will also result in the class cast exception. When you supply the generic parameter, generics are retained and the String glom(Collection<?> objs ) override matches the call (which is passing a List<String>), whereas int glom(List <Integer> ints ) does not match because String is not Integer.

You can use generic to distinguish the method in Java. The JVM doesn't see this type however provided the argument or return type is different it will still compile in the Sun/Oracle compiler. This doesn't compile for the IBM/eclipse compiler.
This shows you want is happening at the byte code level.

Related

How to properly return generic array in Java generic method?

I have below generic method that returns a generic array:
public static <T> T[] genericMethod1(List<T> input) {
T[] res = (T[]) new Object[input.size()];
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
But later when I try to get the result array with:
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(1);
Integer[] i = (Integer[]) genericMethod1(list); // 1) Runtime error
Integer j = genericMethod2(list); // 2) works
For case 1, I always get error at runtime:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Anybody can explain why and how to return the generic array properly? Thanks.
Below is my understanding, please correct me if I'm wrong.
As Tim mentioned, type erasure happened at compile time, so in bytecode, each T object is just type Object, meanwhile, compiler will add type cast from Object to T "properly".
Say T is an Integer, where T is declared, it's Object. For where it's referred, it's type cast (implicitly) to T.
EXCEPT that if T[] array is declared, it's Object[], and where the array is referred, it stays Object[]. No implicit cast to T[] happens.
The explanation for what you are seeing is due to something called type erasure. Here is what your genericMethod() will look like after the compiler performs type erasure:
public static Object[] genericMethod(List input) {
Object[] res = new Object[input.size()];
int i = 0;
for (Object t : input) {
res[i] = t;
i++;
}
return res;
}
In other words, this method will return an array of type Object. There is no way to cast an Object[] to an Integer[] because they are not the same type. If you want your method to be able to dynamically return the type you want, then you can use Array.newInstance(). This will require also passing in the type of the array you want as an input parameter:
public static <T> T[] genericMethod(Class<T> clazz, List<T> input) {
#SuppressWarnings("unchecked")
T[] res = (T[]) Array.newInstance(clazz, input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
Now your code snippet will run without error:
LinkedList<Integer> list = new LinkedList<Integer>();
Integer[] i = genericMethod(Integer.class, list);
Update:
Your second method, genericMethod2(), will look like this after type erasure:
public static Object genericMethod2(List input) {
return input.get(0);
}
It will return the first element of the input list, cast to Object. Here is your usage of that method:
Integer j = genericMethod2(list);
The compiler will try to cast the output from genericMethod2() to Integer:
Integer j = (Integer)genericMethod2(list);
This cast is legal, because every Integer is also an Object, and furthermore it succeeds here because you passed in a collection of Integer. This second method is not the same scenario as the first one you highlighted for us.
When calling the method, genericMethod you are assuming that it returns array of integers, which is NOT correct. It actually returns array of type Object at runtime.
List<Integer> input = new ArrayList<Integer>();
input.add(1);
Object[] output = genericMethod(input);
for(Object obj : output){
System.out.println("Value= "+ (Integer)obj);
}
So we need to cast the individual content of the array.
One general guidline is that we shouldn't mix ARRAY and GENERICS in Java.
Update:
Reference from Effective Java:
In Summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequcne, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time error or warnings, your first impulse should be to replace the arrays with lists.
Another way is to do it like in java.util.ArrayList.toArray(T[]).
You pass the type of Array to that Method, if it's big enough it will be reused, otherwise an new Array is generated.
Example:
List<Integer> intList = new ArrayList<>();
intList.add(Integer.valueOf(1));
intList.add(Integer.valueOf(2));
intList.add(Integer.valueOf(3));
Integer[] array = intList.toArray(new Integer[] {});
System.out.println(Arrays.toString(array));//Will Print [1, 2, 3]
Implementation of ArrayList.toArray(T[]) see here.
After Java 8 was released, you can leverage constructor references to return a generic array. While there are more straightforward options to convert List to Integer[], I am explaining the concept here with kinda minimal changes to your code:
I am not touching your genericMethod2() implementation
For genericMethod1, let's add a second parameter that will accept a constructor reference. Then you can call the apply() function on the constructor reference to create a generic array.
you pass Integer[]::new to this new parameter. Integer[]::new is treated as (int x) => new Integer[x]
fi.apply(input.size()) calls (int x) => new Integer[x] with argument input.size(). The result is a Integer[]. This is why the IntFunction parameter has generic type T[]
import java.util.List;
import java.util.function.IntFunction;
import java.util.LinkedList;
public class RetGenericArr {
public static <T> T[] genericMethod1(List<T> input, IntFunction<T[]> fi) {
T[] res = fi.apply(input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(2);
// umm I am uncomfortable about the variable naming below:
Integer[] i = genericMethod1(list, Integer[]::new); // Now no error/warning. Note that I am not casting now
for (int e: i) {
System.out.println(e);
}
Integer j = genericMethod2(list);
}
}

Unchecked array cast in generic method [duplicate]

I have searched for this, but unfortunately, I don't get the correct answer.
class Helper {
public static <T> T[] toArray(List<T> list) {
T[] array = (T[]) new Object[list.size()];
for (int i = 0; i < list.size(); i++) {
array[i] = list.get(i);
}
return array;
}
}
Test it:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
String[] array = toArray(list);
System.out.println(array);
}
But there is an error thrown:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at test.Helper.main(Helper.java:30)
How to solve this?
UPDATE
I want this method, because sometimes, the type in my code is too long:
newEntries.toArray(new IClasspathEntry[0])
I'd hope to call:
toArray(newEntries)
FINALLY
It seems impossible to create such a method, thank you all very much!
This is due to type erasure. The generics are removed in compilation, thus the Helper.toArray will be compiled into returning an Object[].
For this particular case, I suggest you use List.toArray(T[]).
String[] array = list.toArray(new String[list.size()]);
You can just call list.toArray(T[] array) and not have to worry about implementing it yourself, but as aioobe said, you can't create an array of a generic type due to type erasure. If you need that type back, you need to create a typed instance yourself and pass it in.
If you want to produce your method through brute force, and you can guarantee that you'll only call the method with certain restrictions, you can use reflection:
public static <T> T[] toArray(List<T> list) {
T[] toR = (T[]) java.lang.reflect.Array.newInstance(list.get(0)
.getClass(), list.size());
for (int i = 0; i < list.size(); i++) {
toR[i] = list.get(i);
}
return toR;
}
This approach has problems. As list can store subtypes of T, treating the first element of the list as the representative type will produce a casting exception if your first element is a subtype. This means that T can't be an interface. Also, if your list is empty, you'll get an index out of bounds exception.
This should only be used if you only plan to call the method where the first element of the list matches the Generic type of the list. Using the provided toArray method is much more robust, as the argument provided tells what type of array you want returned.
You can't instantiate a Generic type like you did here:
T[] array = (T[]) new Object[list.size()];
As, if T is bounded to a type, you're typecasting the new Object array to a bounded type T. I would suggest using List.toArray(T[]) method instead.
See Guava's Iterables.toArray(list, class).
Example:
#Test
public void arrayTest() {
List<String> source = Arrays.asList("foo", "bar");
String[] target = Iterables.toArray(source, String.class);
}
public static <T> T[] toArray(Collection<T> c, T[] a) {
return c.size()>a.length ?
c.toArray((T[])Array.newInstance(a.getClass().getComponentType(), c.size())) :
c.toArray(a);
}
/** The collection CAN be empty */
public static <T> T[] toArray(Collection<T> c, Class klass) {
return toArray(c, (T[])Array.newInstance(klass, c.size()));
}
/** The collection CANNOT be empty! */
public static <T> T[] toArray(Collection<T> c) {
return toArray(c, c.iterator().next().getClass());
}
String[] array = list.toArray(new String[0]);
The problem is the component type of the array that is not String.
Also, it would be better to not provide an empty array such as new IClasspathEntry[0].
I think it is better to gives an array with the correct length (otherwise a new one will be created by List#toArray which is a waste of performance).
Because of type erasure, a solution is to give the component type of the array.
Example:
public static <C, T extends C> C[] toArray(Class<C> componentType, List<T> list) {
#SuppressWarnings("unchecked")
C[] array = (C[])Array.newInstance(componentType, list.size());
return list.toArray(array);
}
The type C in this implementation is to allow creation of an array with a component type that is a super type of the list element types.
Usage:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
// String[] array = list.toArray(new String[list.size()]); // Usual version
String[] array = toArray(String.class, list); // Short version
System.out.println(array);
CharSequence[] seqArray = toArray(CharSequence.class, list);
System.out.println(seqArray);
Integer[] seqArray = toArray(Integer.class, list); // DO NOT COMPILE, NICE !
}
Waiting for reified generics..
As pointed earlier this will work:
String[] array = list.toArray(new String[0]);
And this will also work:
String[] array = list.toArray(new String[list.size()]);
However, in the first case a new array will be generated. You can see how this is implemented in Android:
#Override public <T> T[] toArray(T[] contents) {
int s = size;
if (contents.length < s) {
#SuppressWarnings("unchecked") T[] newArray
= (T[]) Array.newInstance(contents.getClass().getComponentType(), s);
contents = newArray;
}
System.arraycopy(this.array, 0, contents, 0, s);
if (contents.length > s) {
contents[s] = null;
}
return contents;
}
Worked solution!
Just copy interface and class inside your project.
This :
public interface LayerDataTransformer<F, T> {
T transform(F from);
Collection<T> transform(Collection<F> from);
T[] toArray(Collection<F> from);
}
and this :
public abstract class BaseDataLayerTransformer<F, T> implements LayerDataTransformer<F, T> {
#Override
public List<T> transform(Collection<F> from) {
List<T> transformed = new ArrayList<>(from.size());
for (F fromObject : from) {
transformed.add(transform(fromObject));
}
return transformed;
}
#Override
public T[] toArray(Collection<F> from) {
Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
T[] transformedArray = (T[]) java.lang.reflect.Array.newInstance(clazz, from.size());
int index = 0;
for (F fromObject : from) {
transformedArray[index] = transform(fromObject);
index++;
}
return transformedArray;
}
}
Usage.
Declare a subclass of BaseDataLayerTransformer
public class FileToStringTransformer extends BaseDataLayerTransformer<File,String> {
#Override
public String transform(File file) {
return file.getAbsolutePath();
}
}
And use :
FileToStringTransformer transformer = new FileToStringTransformer();
List<File> files = getFilesStub();// returns List<File>
//profit!
String[] filePathArray = transformer.toArray(files);
I use this simply function.
IntelliJ just hates that type cast T[] but it works just fine.
public static <T> T[] fromCollection(Class<T> c, Collection<T> collection) {
return collection.toArray((T[])java.lang.reflect.Array.newInstance(c, collection.size()));
}
And call looks like this:
Collection<Integer> col = new ArrayList(Arrays.asList(1,2,3,4));
fromCollection(Integer.class, col);
This gist that I wrote gives a good solution to this problem.
Following siegi's suggestion on Atreys' answer, I wrote a constructor which finds the "nearest common ancestor" (NCA) class and uses that class to create the array. If checks for nulls and if the provided Collection is length 0 or all nulls, the default type is Object. It totally ignores Interfaces.
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Array;
import java.util.Iterator;
public class FDatum<T> {
public T[] coordinates;
// magic number is initial size -- assume <= 5 different classes in coordinates
public transient HashSet<Class> classes = new HashSet<Class>(5);
public FDatum (Collection<T> coordinates) {
// to convert a generic collection to a (sort of) generic array,
// we need to bend the rules:
// 1. default class T is Object
// 2. loop over elements in Collection, recording each unique class:
// a. if Collection has length 0, or
// if all elements are null, class T is Object
// b. otherwise, find most specific common superclass, which is T
// record all unique classes in coordinates
for (T t : coordinates) this.classes.add(t.getClass());
// convert to list so we can easily compare elements
List<Class> classes = new ArrayList<Class>(this.classes);
// nearest common ancestor class (Object by default)
Class NCA = Object.class;
// set NCA to class of first non-null object (if it exists)
for (int ii = 0; ii < classes.size(); ++ii) {
Class c = classes.get(ii);
if (c == null) continue;
NCA = c; break;
}
// if NCA is not Object, find more specific subclass of Object
if (!NCA.equals(Object.class)) {
for (int ii = 0; ii < classes.size(); ++ii) {
Class c = classes.get(ii);
if (c == null) continue;
// print types of all elements for debugging
System.out.println(c);
// if NCA is not assignable from c,
// it means that c is not a subclass of NCA
// if that is the case, we need to "bump up" NCA
// until it *is* a superclass of c
while (!NCA.isAssignableFrom(c))
NCA = NCA.getSuperclass();
}
}
// nearest common ancestor class
System.out.println("NCA: " + NCA);
// create generic array with class == NCA
T[] coords = (T[]) Array.newInstance(NCA, coordinates.size());
// convert coordinates to an array so we can loop over them
ArrayList<T> coordslist = new ArrayList<T>(coordinates);
// assign, and we're done!
for (int ii = 0; ii < coordslist.size(); ++ii)
coords[ii] = coordslist.get(ii);
// that's it!
this.coordinates = coords;
}
public FDatum (T[] coordinates) {
this.coordinates = coordinates;
}
}
Here are some examples of using it in jshell ("unchecked" class warnings removed for brevity):
jshell> FDatum d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3)))
class java.lang.Double
NCA: class java.lang.Double
d ==> com.nibrt.fractal.FDatum#9660f4e
jshell> d.coordinates
$12 ==> Double[2] { 1.0, 3.3 }
jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7)))
class java.lang.Byte
class java.lang.Double
NCA: class java.lang.Number
d ==> com.nibrt.fractal.FDatum#6c49835d
jshell> d.coordinates
$14 ==> Number[3] { 1.0, 3.3, 7 }
jshell> d = new FDatum(new ArrayList(Arrays.asList((double)1, (Double)3.3, (byte)7, "foo")))
class java.lang.Byte
class java.lang.Double
class java.lang.String
NCA: class java.lang.Object
d ==> com.nibrt.fractal.FDatum#67205a84
jshell> d.coordinates
$16 ==> Object[4] { 1.0, 3.3, 7, "foo" }
When you have a generic List<T> you will be able to know the class of the object at the runtime. Therefore, the best way to implement it is like this:
public static <T> T[] list2Array(Class<T[]> clazz, List<T> elements)
{
T[] array = clazz.cast(Array.newInstance(clazz.getComponentType(), elements.size()));
return elements.toArray(array);
}
Why do you need the Class<T[]> parameter?
Because, we have a generic list and it will not provide the information necessary to get an array of precisely the type we are looking for, of course, while preserving type safety. As opposed to the other answers, which will either give you back an Object array or result in warnings at compile time. This approach will gives you a clean solution. The "hack" here is the clazz.cast() call, which compiles without warnings for whatever type you declare an instance of list2Array().
Now, how can you use it?
Simple, just call it like this:
List<String> list = Stream.of("one", "two", "three").collect(Collectors.toList());
String[] numbers = list2Array(String[].class, list);
System.out.println(Arrays.toString(numbers));
Here is the compiling sample of this: https://ideone.com/wcEPNI
Why does it work?
It works because class literals are treated by the compiler as instances of java.lang.Class. This also works for interfaces, enums, any-dimensional arrays (e.g. String[].class), primitives and the keyword void.
Class itself is generic (declared as Class<T[]>, where T[] stands for the type that the Class object is representing), meaning that the type of String[].class is Class<String[]>.
Note: You won't be able to get an array of primitives, since primitives can't be used for type variables.

What does <> do in Java

I am currently new to working with Java. So far I have been able to easily use the basic such as Classes, Functions, Arrays etc from my knowledge of JavaScript and PHP.
However, what I have never seen before is this: <>. Inside of that is a variable of some type. Because I don't know what it is called, I can't find any answers.
I've seen them like this:
List<String> myList = new ArrayList<String>();
But also like:
public static <T> boolean contains( final T[] array, final T v ) {
for ( final T e : array )
if ( e == v || v != null && v.equals( e ) )
return true;
return false;
}
What does the <String> mean?
In the function, I was also wondering what is specifically special about the T?
This is for a Generic type
What it allows you to do is to pass through a type, and make it useful for multiple object types
So List is a generic collection, and it allows you to make a list of any object. Making List<String> will make the object be a list of Strings. Or you could use List<MyClassType> and it would make a list of objects of your class
That defines the Type of the objects a collection can hold
so when you write List<String> myList = new ArrayList<String>();, it means
Create an arrayList that can hold the String objects.
SomeParameterizedClass{
T someValue;
}
means that you can pass your type to the class e.g. SomeParameterizedClass<String> so that someValue becomes of type String
When you write List<T> list = new ArrayList<T>();, here T can be any type.
With java 7 you can simply say List<String> list = new ArrayList<>();. It has the same meaning.
In java 7 <> is called as diamond operator
You will find < > in the following cases, the T is a "parameter type" in plain English that means you could "substitute" that T by any other non-primitive type (like String or Integer):
When you are declaring a parameterized Type:
class MyParameterizedClass<T> {
public T myValue;
}
When you are declaring variables of your parameterized type:
MyParameterizedClass<String> myStringParam;
MyParameterizedClass<Integer> myIntegerParam;
When you are using constructors of parameterized types:
MyParameterizedClass<String> myStringParam = new MyParameterizedClass<String>();
MyParameterizedClass<Integer> myIntegerParam = new MyParameterizedClass<Integer>();
myStringParam.myValue = "Hello world";
myIntegerParam.myValue = 5;
When you declare a generic method:
public <T> T updateMyValue(T myValue) {
this.myValue = myValue
}
What you have observed is partially correct! In Java, inside <> should be 'Object' type or any subclass type of 'Object'.
When you declare myList as below:
List<String> myList = new ArrayList<String>(), it ensures the declared and initialized myList has elements of only String type. This is called type declarations.
public static <T> boolean contains( final T[] array, final T v ) { }, is a generic method declaration which implies that the passed two parameters should be of same object type, 1st parameter an array, 2nd parameter an object. And the method return type also should be same type.
Refer Java docs for more examples on Generics. http://docs.oracle.com/javase/tutorial/extra/generics/methods.html

Can't pass the result of a generic method to another method?

I have a simple generic method that creates a list of n elements and returns it:
import java.util.*;
class A {
public static <T> List<T> init(int n) {
List<T> l = new ArrayList<T>();
while (n --> 0) l.add(null);
return l;
}
public static void main(String[] args) {
List<String> x = init(5);
f(x);
}
public static void f(List<String> l) {
System.out.println("l: " + l);
}
}
It works as expected:
$ javac A.java && java A
l: [null, null, null, null, null]
But if I eliminate the extra variable:
import java.util.*;
class A {
public static <T> List<T> init(int n) {
List<T> l = new ArrayList<T>();
while (n --> 0) l.add(null);
return l;
}
public static void main(String[] args) {
f(init(5));
}
public static void f(List<String> l) {
System.out.println("l: " + l);
}
}
it no longer compiles
$ javac A.java && java A
A.java:9: f(java.util.List<java.lang.String>) in A cannot be applied to (java.util.List<java.lang.Object>)
f(init(5));
^
1 error
Why?
But yet this works:
import java.util.*;
class A {
public static <T> List<T> init(int n) {
List<T> l = new ArrayList<T>();
while (n --> 0) l.add(null);
return l;
}
public static <T> T id(T t) {
return t;
}
public static void main(String[] args) {
List<String> x = init(5);
f(id(x));
}
public static void f(List<String> l) {
System.out.println("l: " + l);
}
}
Why?
Java relies on inference to figure out what the type variables are in a situation where they are not explicitly defined.
In your first example:
List<String> x = init(5);
f(x);
The compiler infers that you are calling <String> init because x is a List<String>.
In your second example:
f(init(5));
The compiler cannot infer that you are calling <String> init because you are not explicitly telling it (via A. <String> init(5)) nor are you assigning it to an appropriate variable.
In your third example:
List<String> x = init(5);
f(id(x));
The compiler infers that you are calling <List<String>> id which returns a List<String> to f.
The compiler isn't too smart about generic inference. Unless you explicitly state what the type arguments are, either by using a variable or by directly passing them to the method, it will not be able to figure them out.
Here are the relevant sections of the JLS if you're curious about the specifics:
JLS §15.12.2.7
JLS §15.12.2.8
If you leave out the extra variable the compiler has no way to infer the type parameter T when calling init(5). It assumes T to be Object and hence the compiler error.
With the extra variable declared as a List<String> x the compiler infer T to be String.
First, the fix;
f(A.<String>init(5)); // compiles
Now, why: The original code compiled because java could infer the type due to being assigned to a typed variable. But inference doesn't work when being passed to a typed parameter.
The fix uses the syntax for explicitly specifying the type when calling a typed method.
f(init(5));
This calls f() with the argument directly received from init(). However, init() now returns a List of T. With T not specified Java just uses Object because it is the base class for all objects. List<Object> is not List<String>, so the method signature and the parameters don't match.
List<String> x = init(5);
f(x);
Here you are putting the List<Object> to a variable of the type List<String>. This converts it, because String is of course a subclass of Object. Also the conversion succeeds because null can also be converted to String as well as any other class. Then x's type matches the method signature.
List<String> x = init(5);
f(id(x));
This is basically the same thing. Now, because x has the type List<String>, id()'s type T is String. This way the return value of id() is also List<String>, which matches the signature.
In both the first and the third example, you stated that you are talking about a List<String>, yet on the second one you said f(init(5)), which could be a List<Integer>. I'm not 100% that's the only reason, but check it out :)

Generic method - "private <T> boolean (T[], T[])"

I'm really new to generics in Java. I'm trying to create a simple method to compare two arrays that would be of the same type, but I want to use a generic method. I've attached a MWE below:
public class MWE {
public static void main(String[] args){
int[] test1,test2;
test1 = new int[2];
test2 = new int[2];
checkArray(test1,test2);
}
private <T> boolean checkArray(T[] check, T[] test) {
if(check.length != test.length)
return false;
for(int i=0;i<check.length;i++)
if(check[i]!=test[i])
return false;
return true;
}
}
When I try to compile, I get:
MWE.java:6: <T>checkArray(T[],T[]) in MWE cannot be applied to (int[],int[])
checkArray(test1,test2);
^
1 error
Generics works only for Objects, you have to have overloaded methods for primitive arrays.
(In which you can switch to Integer[], Boolean[] and so on)
Try using Integer[] instead of int[].
In more detail:
Java Generics always work with classes, so when working with a generic like this, you need to use the class version of each type. Since Java automatically converts an int value to an Integer object value via autoboxing, there really shouldn't be any difference in your code other than the name of the type used.
Change your array types and variables to Integer instead of int. Also, make your checkArray method static.
public class MWE
{
public static void main( String[] args )
{
Integer[] test1 , test2;
test1 = new Integer[ 2 ];
test2 = new Integer[ 2 ];
checkArray( test1 , test2 );
}
private static < T > boolean checkArray( T[] check , T[] test )
{
if ( check.length != test.length )
return false;
for ( int i = 0 ; i < check.length ; i++ )
if ( check[ i ] != test[ i ] )
return false;
return true;
}
}
As others have noted, generics only work with Objects, so technically you could switch to using Integer[], Boolean[] etc.
However, note that Integer[] and Boolean[] extend Object[]. Making this method generic actually has little benefit, since T will always be inferred to at least be Object. For example, the following still compiles:
Integer[] test1 = new Integer[2];
Boolean[] test2 = new Boolean[2];
checkArray(test1,test2);
The caller would need to specify the type parameter for the check to work:
this.<Integer>checkArray(test1,test2); //compile error

Categories