java Generics T and ? difference
1) first we must know this fact: the actual type of an variable is uncertain. a parent class declared variable can actually be a child class type. a parent can represent a child, a child can not represent a parent, it only represent itself or the child of itself.
package test;
import java.util.ArrayList;
import java.util.List;
public class Test<T> {
public static void main(String[] args) {
Object obj;
Number num;
Integer in = 10;
System.out.println("in is " + in.getClass().getCanonicalName());
num = in;
System.out.println("num is " + num.getClass().getCanonicalName());
// java.lang.Integer
obj = in;
System.out.println("obj is " + obj.getClass().getCanonicalName());
// java.lang.Integer
List<Object> objList = new ArrayList<Object>();
List<Number> numList = new ArrayList<Number>();
List<Integer> inList = new ArrayList<Integer>();
inList.add(in);
System.out.println("inList is " + inList.getClass().getCanonicalName());
System.out.println("inList.get(0) is " + inList.get(0).getClass().getCanonicalName());
numList.add(in);
System.out.println("numList is " + numList.getClass().getCanonicalName());
System.out.println("numList.get(0) is " + numList.get(0).getClass().getCanonicalName());
objList.add(in);
System.out.println("objList is " + objList.getClass().getCanonicalName());
System.out.println("objList.get(0) is " + objList.get(0).getClass().getCanonicalName());
}
}
in is java.lang.Integer
num is java.lang.Integer
obj is java.lang.Integer
inList is java.util.ArrayList
inList.get(0) is java.lang.Integer
numList is java.util.ArrayList
numList.get(0) is java.lang.Integer
objList is java.util.ArrayList
objList.get(0) is java.lang.Integer
2) should we let a List<Object>
declared variable represent a List<Integer>
type ? The answer is NO. let us think about this.
List<Object> objList1 = new ArrayList<Object>();
List<Object> objList2 = new ArrayList<Number>();//if it can be true here then next step will be error.
objList2.add(new Object);//error! objList2 actually is a List< Number > !
3) so we have no choice! let List<Object> = new List<Object>
is the only way to keep safe.
4) but we really want a syntax to let List<Parent>
variable represent List<Child>
, because there are too many children type. child1, child2, child3, .... we want a syntax to let variable represent one of List< child1>
, List<child2>
, List<child3>
, ... be careful. we know that List<child1>
and List<child2>
can not represent each other. but we want to declare that List<? extends Parent>
can represent List<child1>
when it is = new List<child1>()
. or it can represent List<child2>
when it is = new List<child2>()
. etc. when we see List<? extends Parent> g
,we know that g actually is a List<child1>
or a List<child2>
or a List<child3>
or List< Parent >
. one of them , not any of them! so we use the ? extends Parent
instead of * extends Parent
.
5) because List<? extends Parent> g
actually is List<ChildX>
, we can read from List<ChildX>
use a Parent
type variable. we can not put a Parent
or a Child
to its element position. because actually the element position may be Child1
type. maybe Child2
type... anyway, it is uncertain. what we know is it must look like a Parent type. we can read from the element position then use it as a Parent. but we can not replace it. if we do so ,we may break the rule to make Child <= Parent
happens! or Child1 <= Child2
. that must be forbidden!
6) then let us see List<? super Parent> g
. it represent a List<P1>
, or List<P2>
, or List<P3>
, ... which P1 is parent of Parent, P2 is parent of Parent, P3 is parent of Parent...
7) we can safely set a child of Parent to the g's element just like Parent p = new Child()
. also we face the uncertainty. so we can not actually read from the element unless we use Object p = g.get(0)
.
8) when to use ? let us think about this scenario: we want a List<T> => List<T,T> convert
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DoubleElement<T> {
public final T x;
public final T y;
public DoubleElement(T e) {
this.x = e;
this.y = e;
}
@Override
public String toString() {
return "{x:" + x + ", y:" + y + "}";
}
public static <T> List<DoubleElement<T>> toDoubleElementArray(List<T> list) {
List<DoubleElement<T>> doubleElementArray = new ArrayList<DoubleElement<T>>(list.size());
for (T e : list) {
doubleElementArray.add(new DoubleElement<T>(e));
}
return doubleElementArray;
}
public static void main(String[] args) {
List<Integer> inList = Arrays.asList(new Integer[] { 1, 2, 3 });
List<Number> numList = Arrays.asList(new Number[] { 1, 2.5f, 3 });
List<Float> floatList = Arrays.asList(new Float[] { 1.1f, 2.2f, 3.3f });
List<DoubleElement<Integer>> doubleInList = DoubleElement.toDoubleElementArray(inList);
System.out.println(doubleInList);
List<DoubleElement<Number>> doubleNumList = DoubleElement.toDoubleElementArray(numList);
System.out.println(doubleNumList);
List<DoubleElement<Float>> doubleFloatList = DoubleElement.toDoubleElementArray(floatList);
System.out.println(doubleFloatList);
//we want to constraint this because we only want to use Number and its Child to work.
List<String> strList = Arrays.asList(new String[] { " I ", " am ", " evil !" });
List<Object> objList = Arrays.asList(new Object[] { " I ", " am ", " evil !", " too !" });
List<DoubleElement<String>> doubleStrList = DoubleElement.toDoubleElementArray(strList);
System.out.println(doubleStrList);
List<DoubleElement<Object>> doubleObjList = DoubleElement.toDoubleElementArray(objList);
System.out.println(doubleObjList);
}
}
[{x:1, y:1}, {x:2, y:2}, {x:3, y:3}]
[{x:1, y:1}, {x:2.5, y:2.5}, {x:3, y:3}]
[{x:1.1, y:1.1}, {x:2.2, y:2.2}, {x:3.3, y:3.3}]
[{x: I , y: I }, {x: am , y: am }, {x: evil !, y: evil !}]
[{x: I , y: I }, {x: am , y: am }, {x: evil !, y: evil !}, {x: too !, y: too !}]
T works fine. but we want to add a constraint to T. only List<Number>
and its Child List<Float>
List<Integer>
... can use this method. how to ?
public static <T> List<DoubleElement<Number>> toDoubleElementArray(List<Number> list) {
//this will well done with List<Number> but not List<Float> or List<Integer> or ...
yes! it is showtime of List<? extends Number>
!
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DoubleElement<T> {
public final T x;
public final T y;
public DoubleElement(T e) {
this.x = e;
this.y = e;
}
@Override
public String toString() {
return "{x:" + x + ", y:" + y + "}";
}
public static List<DoubleElement<? extends Number>> toDoubleElementArray(List<? extends Number> list) {
List<DoubleElement<? extends Number>> doubleElementArray = new ArrayList<DoubleElement<? extends Number>>(
list.size());
for (Number e : list) {
doubleElementArray.add(new DoubleElement<Number>(e));
}
return doubleElementArray;
}
public static void main(String[] args) {
List<Integer> inList = Arrays.asList(new Integer[] { 1, 2, 3 });
List<Number> numList = Arrays.asList(new Number[] { 1, 2.5f, 3 });
List<Float> floatList = Arrays.asList(new Float[] { 1.1f, 2.2f, 3.3f });
List<DoubleElement<? extends Number>> doubleInList = DoubleElement.toDoubleElementArray(inList);
System.out.println(doubleInList);
List<DoubleElement<? extends Number>> doubleNumList = DoubleElement.toDoubleElementArray(numList);
System.out.println(doubleNumList);
List<DoubleElement<? extends Number>> doubleFloatList = DoubleElement.toDoubleElementArray(floatList);
System.out.println(doubleFloatList);
//we want to constraint this because we only want to use Number and its Child to work.
List<String> strList = Arrays.asList(new String[] { " I ", " am ", " evil !" });
List<Object> objList = Arrays.asList(new Object[] { " I ", " am ", " evil !", " too !" });
//this will compile error!
List<DoubleElement<String>> doubleStrList = DoubleElement.toDoubleElementArray(strList);
System.out.println(doubleStrList);
//this will compile error!
List<DoubleElement<Object>> doubleObjList = DoubleElement.toDoubleElementArray(objList);
System.out.println(doubleObjList);
}
}
9) at last. let us see an example of <? super T>
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.util.function;
import java.util.Objects;
/**
* Represents a function that accepts one argument and produces a result.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object)}.
*
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
*
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
package test;
import java.util.function.Function;
public class TestFunction {
public static void main(String[] args) {
Function<Integer, Float> func = e -> {
return e + 5.5f;
};
Function<Number, Number> func2 = e -> {
return Math.round(e.floatValue());
};
//OK round( e+5.5 )
Number result = func.andThen(func2).apply(5);
System.out.println(result);
//this will constraint you because the <? super Float>
Function<Integer, Number> func3 = e -> {
return Math.round(e.floatValue());
};
//compile error!
Number result3 = func.andThen(func3).apply(5);
System.out.println(result);
}
}
10) that's all. let us have a good weekend!
Author And Source
この問題について(java Generics T and ? difference), 我々は、より多くの情報をここで見つけました https://qiita.com/siumennel/items/bc88d35c299df39b6aa0著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .