java Generics T et? Différence

  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.

Test.java


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
  1. should we let a List<Object> declared variable represent a List<Integer> type ? The answer is NO. let us think about this.

python


		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 > !
  1. so we have no choice! let List<Object> = new List<Object> is the only way to keep safe.

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

  3. 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!

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

  5. 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).

  6. when to use ? let us think about this scenario: we want a List<T> => List<T,T> convert

DoubleElement.java


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

DoubleElement.java


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);

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

TestFunction.java


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);
	}
}
  1. that's all. let us have a good weekend!

Recommended Posts

java Generics T et? Différence
[Java] Différence entre == et égal
[Java] Classe générique et méthode générique
[Java] Génériques
[Java] Différence entre Hashmap et HashTable
[JAVA] Différence entre abstrait et interface
[Java] Différence entre array et ArrayList
Java Generics (définit les classes et les méthodes)
[Java] Différence entre fermable et fermable automatiquement
[Java] Différence entre StringBuffer et StringBuilder
[Java] Différence entre longueur, longueur () et taille ()
Résumé des génériques Java
Différence entre final et immuable en Java
[Java] Exemple de génériques
[Pour les débutants] Différence entre Java et Kotlin
Java et JavaScript
XXE et Java
Différence entre les listes d'arry et les listes liées en Java
[Java] Différence entre la plage Intstream et la plageClosed
Différence entre int et Integer en Java
[Java] Comprendre la différence entre List et Set
Différence entre next () et nextLine () dans Java Scanner
[Java] Différence entre "variable finale" et "objet immuable"
Getter et Setter (Java)
[Java] Thread et exécutable
Java vrai et faux
[Java] Comparaison des chaînes de caractères et && et ||
Java - Sérialisation et désérialisation
[Java] Arguments et paramètres
[Java] Branchement et répétition
Différence entre vh et%
[Java] Types de variables et types
Différence entre i ++ et ++ i
java (classe et instance)
[Java] Surcharge et remplacement
[Java] Différence entre statique final et final dans les variables membres
[JAVA] Quelle est la différence entre interface et abstract? ?? ??
À propos des setters et des getters Java. <Différence par rapport à l'orientation de l'objet>
[Java10] Veillez à utiliser ensemble var et génériques
[Java débutant] Différence entre la longueur et la longueur () ~ Je ne sais pas ~
Etudier Java # 2 (\ marque et opérateur)
Différence entre produit et variante
Java version 8 et fonctionnalités ultérieures
Différence entre redirect_to et render
[Java] Zone de pile et zone statique
Programmation Java (variables et données)
Cryptage et décryptage Java PDF
Différence entre redirect_to et render
Définition et instanciation de classe Java
Différence entre les variables et les variables d'instance
Différence entre mockito-core et mockito-all
[Java] À propos de String et StringBuilder
[Java] HashCode et remplacement égal
☾ Instruction Java / Repeat et instruction de contrôle de répétition
[Java] Différence entre l’affectation d’une variable de type de base et l’affectation d’une variable de type de référence
Différence entre classe et instance
Différence entre l'installation de bundle et de bundle
[Java] Vérifiez la différence entre orElse et orElseGet avec IntStream