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));
}
}
Related
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
This question already has answers here:
Return Type of Java Generic Methods
(5 answers)
Closed 6 years ago.
I am reading generics and tried writing the below code. There are no compilation error.
import java.util.*;
public class JavaApplication14 {
public<T> void run (T obj ) {
//Do Something
}
public static void main(String[] args) {
JavaApplication14 m= new JavaApplication14();
m.run(new ArrayList<>());
m.run(new Interger(5);
m.run(5);
}
}
If the function is
public<T extends Number> void run (T obj) {
//Do Something
}
It makes sense as we can restrict the arguments of this function to a Number and its subtypes. But am terribly confused what the function 'run' without any bound mean?
Can it now take any object as the argument ? In what scenario do i need to use such a function with generics ?
Part of your confusion may stem from the fact that there is no point in having run be a generic method in this case. You normally use a type parameter to create a relationship between two parameter types and/or between parameter type and return type. In your example run could just as well have been declared as requiring an Object parameter (a type parameter without a declared bound effectively has Object as its bound).
There is one case I know of where you might usefully use a type parameter in a single parameter type: when you want to be able to manipulate a collection in a way that doesn't depend on the element type, but which does require inserting elements into the collection. Consider for example a hypothetical "reverse list" method:
<T> void reverse(List<T> list)
{
List<T> reversed = new ArrayList<T>();
for (int i = list.size(); i > 0; i--) {
reversed.add(list.get(i - 1));
}
list.clear();
list.addAll(reversed);
}
It would be difficult to write this in a way that didn't require a type parameter, i.e. that takes a List<?> parameter. The only way to do it without casts is to do:
void reverse2(List<?> list)
{
reverse(list); // call reverse as defined above!
}
But again, this doesn't apply in the example you discuss.
So in summary:
A type parameter without an explicit bound effectively has an Object bound.
There are two reasons why a method might need a type parameter (either with or without an explicit bound):
Specify a relationship between parameter types and/or return type
Capture a potential wildcard as a type parameter to allow operations that wouldn't otherwise be possible (as in the reverse example).
The example method you discussed:
public<T> void run (T obj )
... does neither of these, and so the type parameter is pointless. The method might just as well have been declared as public void run(Object obj).
It allows you to avoid any cast.
public class SomeClass {
void doStuff();
}
public<T extends SomeClass> void run (T obj) {
//can call doStuff without any casting
obj.doStuff();
}
public<T> void run (T) {
//Here, there's no clue to perform the implicit cast.
obj.doStuff(); //won't compile
}
While in this case the function could take Object just as well, the variant that makes sense to you is equivalent to public void run(Number obj) { ... } as well. For an example where lack of bound makes sense consider a case where the return type mentions T: public <T> List<T> singletonList(T obj).
Some theory
There're generic methods. Their main goal is generic algorithms (receive and return same types).
Code that uses generics has many benefits over non-generic code:
Elimination of casts.
Stronger type checks at compile time.
Enabling programmers to implement generic algorithms.
A little practice
Consider the following code:
class MyClass {
public void method() {}
public static void main(String[] args) {
runFirst(new MyClass());
runSecond(new MyClass());
}
public static <T extends MyClass> void runFirst(T obj) {
obj.method();
}
public static <T> void runSecond(T obj) {
((MyClass) obj).method();
}
}
The runFirst() method allows us to avoid cast to class and all its subclasses. In runSecond() method we can get any type of parameter (<T>, roughly speaking, means <T extends Object>). Firstly, we must cast to MyClass and then call its method.
First of all I will start with the meaning of public <T> void run(T object) { ... }. Yes when you use that kind of code you than you may use any object as a parameter of run. If you want to restrict the arguments of this function to a specific interface, class or its sub classes you can just write code like NotGenericRun which is shown below.
public class NotGenericRun {
public void run(ArrayList<?> list) {
String message = "Non Generic Run List: ";
System.out.println(message.concat(list.toString()));
}
public void run(int intValue) {
String message = "Non Generic Run Int: ";
System.out.println(message.concat(String.valueOf(intValue)));
}
}
Here I tested output of GenericRun and NotGenericRun classes.
public class TestClass {
public static void main(String[] args) {
GenericRun m = new GenericRun();
m.run(new ArrayList<>());
m.run(new Integer(5));
m.run(5);
NotGenericRun n = new NotGenericRun();
n.run(new ArrayList<>());
n.run(new Integer(5));
n.run(13);
}
}
Output of this code was following:
Generic Run: []
Generic Run: 5
Generic Run: 5
Non Generic Run List: []
Non Generic Run Int: 5
Non Generic Run Int: 13
When you use Generic run as I already said arguments may be any object but there is other way of restricting the arguments while still using generics.
public class GenericRun {
public <T> void run(T object) {
String message = "Generic Run: ";
System.out.println(message.concat(object.toString()));
}
}
This is how.
public class GenericRun <T> {
public void run(T object) {
String message = "Generic Run: ";
System.out.println(message.concat(object.toString()));
}
}
In this case you'll be using GenericClass like this:
GenericRun<Integer> m = new GenericRun<Integer>();
m.run(new Integer(5));
m.run(5);
and only value that it will be tacking should be stated while creating class. I can't think of scenario when public <T> void run(T object) { ... } may be needed but it might occur when you'll need the method to get every argument or you don't know what arguments will be (but it's really less likely). I think more often when you'll be using generics with run like this:
public class GenericRun <T> {
public void run(T object) {
...
}
}
I was searching about usage of generic methods here you can read more about why may we need generic methods.
Here is another example:
public class GenericRun {
public <T> void run(T[] inputArray) {
for (T element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
}
Using this class you can print array of different type using a single Generic method:
public class TestClass {
public static void main(String[] args) {
GenericRun m = new GenericRun();
// Create arrays of Integer, Double and Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println("Array integerArray contains:");
m.run(intArray); // pass an Integer array
System.out.println("\nArray doubleArray contains:");
m.run(doubleArray); // pass a Double array
System.out.println("\nArray characterArray contains:");
m.run(charArray); // pass a Character array
}
}
I hope I answered your question.
The only thing makes sense here is if this was some kind of pseudo-abstract or base class, that provided framework for behaviour and let another coders implement their own logic, but also provided default null action.
It could allow for better generic type-setting, for example as:
class MySubClass extends JavaApplication14 {
public <T> void run(T obj){
new ArrayList<T>().add(obj);
}
}
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});
}
}*
I'm having some problems with a method returning a generic list. The code is basically this:
public class MyClass{
private List<MyListElement> myList = new ArrayList<MyListElement>();
public <E> List<E> getGenericList(){
return new ArrayList<E>();
}
public void thisWorks(){
List<MyListElement> newList = getGenericList();
myList.addAll(newList);
}
public void thisDoesntWork(){
myList.addAll(getGenericList());
}
public void thisDoesntWorkEither(){
for(MyListElement elem : getGenericList()){
fiddle();
}
}
}
Why does the thisDoesntWork() method not work, and is there any other way around it (other than doing it the thisWorks() way which isn't always practical)?
The compiler cannot infer what type to choose for the type parameter <E> of the generic method getGenericList() in thisDoesntWork().
In this case you need to explicitly state the Type for the type argument by calling <MyListElement>getGenericList()
Alternatively you can change the signature of getGenericList() to accept a Class<E> argument. Then you would invoke getGenericList(MyListElement.class) in both thisWorks() and thisDoesntWork(). Admittedly that's a bit more verbose, but definitly more intuitive to clients of your method.
I would say as a general rule, try to make the type arguments of your generic methods be inferrable from that method's arguments.
You can change thisDoesntWork() like so:
public void thisDoesntWork(){ // well, it works now
myList.addAll(this.<String>getGenericList());
}
You need to tell the compiler what type getGenericList() is dealing with.
The type argument to the generic method <E> getGenericsList() can be passed at call time:
this.<String>getGenericsList()
otherwise the compiler does its best to deduce it from the context. When you assign the returned object to a List<String> reference, the compiler hence infers that you passed String as the type argument.
Given the List API:
List<E> {
void addAll(Collection<? extends E> c);
}
the compiler doesn't seem to be able to infer the correct type, and to be honest I don't know if this is because it is not smart enough, or because it doesn't want to carry the responsibility by design.
I even made a test to see if the problem is the wildcard, or if addAll() cannot infer the type arguments from the parameterized type, but nothing seems to work:
public class GenericsList {
public static void main(String[] args) {
// same signature as Java API
List<String> base = new List<String>();
base.addAll(GenericsList.getList()); // ERROR!
// addAll() doesn't use a wildcard
List2<String> base2 = new List2<String>();
base2.addAll(getList2()); // ERROR!
// what about a generic method?
addAll(base, getList()); // ERROR!
}
static <E> List<E> getList() {
return new List<E>();
}
static <E> void addAll(List<E> src, List<E> other) {}
static <E> List2<E> getList2() {
return new List2<E>();
}
static class List<E> {
void addAll(List<? extends E> other) {}
}
static class List2<E> {
void addAll(List2<E> other) {}
}
}
This code will work for you.
getGenericList() should know what it is returning and that should be compatible with your list type.
You need not cast it to String or any other as suggested by others as it will restrict your getGenericList method intention by tying it to string type.
public class MyClass{
private List<MyListElement> myList = new ArrayList<MyListElement>();
public <E extends MyListElement> List<E> getGenericList(){
return new ArrayList<E>();
}
public void thisWorks(){
List<MyListElement> newList = getGenericList();
myList.addAll(newList);
}
public void thisDoesntWork(){
myList.addAll(getGenericList());
}
public void thisDoesntWorkEither(){
for(MyListElement elem : getGenericList()){
fiddle();
}
}
}
When writing a generic method to process data for a form, I came across with the following (as I see it) unexpedted behavior. Given the following code:
public class Test {
public <T> void someGenericMethod(Integer a) {
#SuppressWarnings("unchecked")
T t = (T) a;
System.out.println(t);
System.out.println(t.getClass());
}
public static void main(String[] args) {
Test test = new Test();
test.<BigDecimal>someGenericMethod(42);
}
}
AFAIK, the code above should generate a ClassCastException in the line T t = (T) a because the method call in main is setting the parametrized type to BigDecimal and casting from Integer to BigDecimal is not allowed, conversely to what I expected, the program executed well and printed the following:
42
class java.lang.Integer
In fact, if I add another parameter to the method signature (like String b) and make another assigment T t2 = (T) b, the program prints
42
class java.lang.String
Why the t variable changed it's type to Integer (is, by any chance, making some kind of promotion on the type T to Object)?
Any explanation on this behavior is welcome
(T) a is an unchecked cast: due to type erasure, the runtime has no way of knowing what type T is, so it can't actually check if a belongs to type T.
The compiler issues a warning when you do this; in your case, you've suppressed that warning by writing #SuppressWarnings("unchecked").
Edited to add (in response to a further question in the comments below):
If you want to check the cast, you can write this:
public class Test {
public <T> void someGenericMethod(Class<T> clazz, Integer a) {
T t = clazz.cast(a);
System.out.println(t);
System.out.println(t.getClass());
}
public static void main(String[] args) {
Test test = new Test();
// gives a ClassCastException at runtime:
test.someGenericMethod(BigDecimal.class, 42);
}
}
by passing in clazz, you allow the runtime to check the cast; and, what's more, you allow the compiler to infer T from the method arguments, so you don't have to write test.<BigDecimal>someGenericMethod anymore.
Of course, the code that calls the method can still circumvent this by using an unchecked cast:
public static void main(String[] args) {
Test test = new Test();
Class clazz = Object.class;
test.someGenericMethod((Class<BigDecimal>) clazz, 42);
}
but then that's main's fault, not someGenericMethod's. :-)
When compiling, your code above basically becomes the following non-generic method:
public void someGenericMethod(Integer a) {
Object t = a;
System.out.println(t);
System.out.println(t.getClass());
}
There is no cast. No exception.
You specify a type parameter in your method signature, but never use it.
I think you want something like this:
public class Test {
public <T> void someGenericMethod(T someItem) {
System.out.println(someItem);
System.out.println(someItem.getClass());
}
}
public static void main(String[] args) {
Test test = new Test();
BigDecimal bd = new BigDecimal(42);
test.someGenericMethod(42); // Integer
test.someGenericMethod("42"); // String
test.someGenericMethod(42L); // Long
test.someGenericMethod(bd); // BigDecimal
}
Note that there's no need to cast.
The parameter type is declared in the method signature and inferred from the parameter.
In your code you're parameterizing the method call (which I've never seen) and passing in an int.
It's kinda hard to understand what you're trying to do, since your example code does nothing.