Generics, passing wrong types? - java

What is the difference between the last two statements ? why does one statement work and the other doesn't ?
package Main;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void printIt(List<Object> l)
{
System.out.println(l);
}
public static void main(String[] args) {
List<String> l =new ArrayList<>();
l.add("A");
//what is the differance between the following statments ?
printIt(Arrays.asList("A")); // it compiles successfully
printIt(l); // it does not compile
}
}

The problem is printIt() method expects List<Object> as parameter but we are passing List<String> to it, that's why there is compilation problem.
Replace the parameter List in method printIt() as below:
public static void printIt(List<? extends Object> l)
{
System.out.println(l);
}
Now both will compile,

This is because your method expects a List<Object> and you give it a List<String>.
As weird as it can appear the first time you read this, a List<String> is not a List<Object>.
In your example, you don't modify the content of the lists but let's imagine a method where you want to add a new element.
public static void addIt(List<Object> l, Object o)
{
l.add(o);
}
public static void main(String[] args) {
List<String> l =new ArrayList<>();
l.add("A");
addIt(l, new Integer(1)); // What?! you want to add an Integer to a List<String>!!!!
}
You will have to use wildcards (?) or to solve your problem so your your List .
public static void printIt(List<?> l) //or printIt(List<? extends Object> l)
{
System.out.println(l);
}
The case of printIt(Arrays.asList("A")) is a bit different. It is due to the fact that the generic is determined dynamically, by type inference on a generic method.
List<Object> l = Arrays.asList("A"); //this is valid, the generic type is determined from the type we expect in this declaration.

To help you grok this, here is another compiling code example that demonstrated type inference from return type:
import java.util.*;
// our main class becomes a file but the main method is still found
public class HelloWorld
{
public static void main(String[] args)
{
foo(createList()); // T must be Object
}
private static void foo(List<Object> objs) { }
private static <T> List<T> createList() { return new ArrayList<>(); }
}

so as #csharpfolk mentioned it is all about "Type Inference"!
the following document could be helpful to understand the idea behind that!
https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

Related

How to write a method that takes as input a generic type within a parameterized type?

Can someone explain to me why this is okay:
public static void main(String args[]) {
List<Integer> integers = new ArrayList<>();
test(integers);
}
public static void test(List list) {
}
But this creates the error in the comment:
public static void main(String args[]) {
List<List<Integer>> integers = new ArrayList<>();
test(integers);
//'test(java.util.List<java.util.List>)' cannot be applied to '(java.util.List<java.util.List<java.lang.Integer>>)'
}
public static void test(List<List> lists) {
}
In my understanding, in both cases, the program casts a parameterized type to a generic type, but it only works in the first case. Why is this and how do I go about making a method with a parameter like the second example that could take any list of lists as input?
What you're looking for is
public static <T> void test(List<List<T>> lists) {
}
or
public static void test(List<? extends List<?>> lists) {
}
Don't ever have List without a < after it, except when importing it. It has strange properties and removes type safety. Having List<List> puts the compiler into a weird state where it's trying to have some level of type safety and some not.

I am having some wrong concept about wildcard in Java

I am trying to understand how to use wildcard in Java.
import java.util.List;
public class WildcardError{
void foo(List<?> i){
i.set(0, i.get(0));
}
}
public class WildcardFixed{
void foo(List<?> i){
fooHelper(i);
}
private <T> void fooHelper(List<T> l){
l.set(0, l.get(0));
}
}
Why the first class cannot be compiled whereas the second one can?
What happen when compiler sees the wildcard? turns it into Object? like void foo(Object i) in the first class, or void foo(List<Object> i).
How type inference works in the second class ?
In the first example you could have passed a List<String>, so if you set an element with an Object (the result of l.get(0)), you cannot actually do that type-safely. A bit of a myopic view.
In the second example, l.get(0) is a T, and fits perfectly in the l.set,
Good tutorial explaining this https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
...
It isn't safe to add arbitrary objects to it however:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.
On the other hand, given a List, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.
When I do this:
import java.util.ArrayList;
import java.util.List;
public class Wildcard {
public static void main(String[] args) {
WildcardFixed wf = new WildcardFixed();
List<Integer> li = new ArrayList<>();
li.add(0);
wf.foo(li);
System.out.println("Success");
}
}
class WildcardError{
void foo(List<?> i){
i.set(0, i.get(0));
}
}
class WildcardFixed{
void foo(List<?> i){
fooHelper(i);
}
private <T> void fooHelper(List<T> l){
l.set(0, l.get(0));
}
}
The WildcardError class fails to compile with message
The method set(int, capture#1-of ?) in the type List is not applicable for the arguments (int, capture#2-of ?)
In plainer English, the compiler is saying that it doesn't know what type of thing is contained in i, i.e. it doesn't know what type is returned by the get(), and it doesn't know what type of argument the set() takes, so it can't guarantee that the set() operation will succeed.
WildcardFixed, however, compiles simply because we reassure the compiler that, whatever type is in l, the result of the get will be the same type, T, as the argument type of the set. The compiler doesn't need much to go on, but it needs more than ?.
You can get simpler, though. If you put the type parameter T into the original foo() method, everything compiles and runs perfectly.
import java.util.ArrayList;
import java.util.List;
public class Wildcard {
public static void main(String[] args) {
WildcardFixed wf = new WildcardFixed();
List<Integer> li = new ArrayList<>();
li.add(0);
wf.foo(li);
System.out.println("Success WildcardFixed");
WildcardWithT wt = new WildcardWithT();
wt.foo(li);
System.out.println("Success WildcardWithT");
}
}
class WildcardError{
void foo(List<?> i){
i.set(0, i.get(0));
}
}
class WildcardFixed{
void foo(List<?> i){
fooHelper(i);
}
private <T> void fooHelper(List<T> l){
l.set(0, l.get(0));
}
}
class WildcardWithT {
<T> void foo(List<T> i) {
i.set(0, i.get(0));
}
}

Get generics parameter for new instance

I need to make an instance of generic class. Do not know what to use as parameter.
import java.util.List;
import java.util.ArrayList;
public class Main{
public static void main(String[] args){
Class type = someMethod(); //It could return String.class, Integer.class, MyClass.class etc
//Now I need to make a instance of something using generic parameter. I use List as example here.
List<type)> list = new ArrayList<>(); //Does not work, obvioulsy since it looks weird.
//But what does work?
}
}
You can make compiler ensure compile-type (not runtime) safety for you by passing desired class of List elements:
public static <T> List<T> makeList(#SuppressWarnings("UnusedParameters") Class<T> elClass) {
return new ArrayList<>();
}
public static void main(String[] args) {
List<String> strings = makeList(String.class);
List<Integer> ints = makeList(Integer.class);
}

How to create a List using the paremetrized type of the generic class as the datype or Object?

Is it anyway possible to create a list by using the parametrized TYPE
of the generic class? In the below code T is the parametrized type.
Any possible solutions?
package collectionsDemo;
import java.util.*;
public class ListDemo<T> {
public void listCreator(){
List<T> listDemo=new ArrayList<>();
/*i intended to add something like below Since
T is type of Integer Object,which i can't perform right now.
and below is the compile time error*/
listDemo.add(34);
listDemo.add(55);
/*The method add(int, T) in the type List<T> is not
applicable for the arguments (int)*/
}
public static void main(String[] args) {
ListDemo<Integer> listdemo=new ListDemo<>();
}
}
No. This is not the way you can do it.
You can not store an int in a list that wants a element of type T.
ListDemo<T> and List<T>, in both these cases, the type parameter T is different.
You need to do this:
List<Integer> listDemo = new ArrayList<>();
Then, and only Then, you cad add integers in your list.
I have been able to answer my own question and i am not sure whether it is best practice or not?Correct me if i am wrong!
*package collectionsDemo;
import java.util.*;
import java.util.Scanner;
public class GenericListCreator<T> {
public void listCreator( T[] e){
List<T> listDemo=new ArrayList<>();
for(T t:e){
listDemo.add(t);
}
for(T t:e){
System.out.println(t);
}
}
public static void main(String[] args) {
GenericListCreator<Integer> age=new GenericListCreator<>();
age.listCreator(new Integer[]{21,22,24});
GenericListCreator<String> name=new GenericListCreator<>();
name.listCreator(new String[]{"John","michell","bing"});
GenericListCreator<Double> price=new GenericListCreator<>();
price.listCreator(new Double[]{23.5,556.5,55.6});
}
}*

How can I pass a dataType as an argument to create an ArrayList of this type?

Let's say I have something like:
public void do(DataType type) {
ArrayList<DataType> list = new ArrayList<DataType>();
doStuff();
}
In some cases I want to create an arrayList of another type by passing the type as a parameter, how can I do this in Java?
try this..
public <T> void stuff(T type){
ArrayList<T> arrayList = new ArrayList<T>();
doStuff();
}
generics may be helpfull you if you want to create sigle object that represent it, but when you want a simple private attribute the easy way is this.
I assume you want something like this:
public static <T>List<T> makeList(T t) {
List<T> list = new ArrayList<T>();
// doStuff();
return list;
}
And usage of makeList function:
makeList("String list").add("new String"); // ok
makeList("String list").add(5); // compile error
More universal approach is the following:
public <T extends IDataType> void doSomething(T type) {
ArrayList<T> list = new ArrayList<T>();
doStuff();
}
where: DataType implements IDataType
In this case you can use any type which implements IDataType
Java doesn't allow to pass class types as method parameters. As far as I know, the closest you can get is this :
import java.util.*;
class MyClass<T> {
public void stuff() {
ArrayList<T> list = new ArrayList<T>();
//doStuff();
}
}
class Test {
public static void main(String args[]) {
(new MyClass<String>()).stuff();
(new MyClass<Integer>()).stuff();
}
}
So you would need to instanciate the class each time you need it.
Are you looking for something like this?
public static <T> List<T> doSomething(Class<T> clazz){
ArrayList<T> testList = new ArrayList<T>();
return testList;
// more code
}
and you can invoke this like :
List<Integer> intList = doSomething(Integer.class);
intList.add(1);
List<String> stringList = doSomething(String.class);
stringList.add("test");
and if you want to restrict the type of Class you need, you could use T extends youDataType. If what #subhash has explained is not the one you are looking for, and if what #zoyd explains is true, then i believe this could be of some use.

Categories

Resources