java_泛型结构模型整理

从特殊情况:

在这里插入图片描述

抽象出较为一般的情况:

在这里插入图片描述

类型通配符

1、类型通配符一般是使用?代替具体的类型参数。

例如 List<?>:

 在逻辑上是List<String>,List<Integer> 等所有List<具体类型实参>的父类。

2、类型通配符上限通过形如class<? extends type>来定义
例如List<? extends Number>
如此定义就是通配符泛型值接受Number及其下层子类类型。

3、类型通配符下限通过形如 Class<? super type>来定义
例如List<? super Number>
表示类型只能接受Number及其父类类型,如 Object 类型的实例。

两个实例

package generic;

/**
 * @Author xuchaoxin
 * @Date 2021/1/29 23:20
 * @Version 1.0
 * the detail and improved version(if I update it ) to see:
 * the github repository (search the article):https://github.com/xuchaoxin1375/LearnJava
 */
public class Box<T> {
    
    
    private T t;
    public void add(T t) {
    
    
        this.t = t;
    }
    public T get() {
    
    
        return t;
    }
    public static void main(String[] args) {
    
    
        /*为泛型类box<T>指定具体的类型T,使得Box<T>实例化出来的对象也是类型具体的.而且调用该类中的方法传入的参数也是类型确定的*/
        Box<Integer> integerBox = new Box<Integer>();
        Box<String> stringBox = new Box<String>();
        /* 调用泛型方法*/

        /*generic.Box<T> public void add(T t)*/
        /*由于integerBox和stringBox都是类型具体的,故而对它们调用的方法所传入的参数也是类型确定而且于被调用对象的类型相对应.*/
        integerBox.add(10);
        stringBox.add("test for generic.");
        System.out.printf("整型值为 :%d\n\n", integerBox.get());
        System.out.printf("字符串为 :%s\n", stringBox.get());
    }
}

package generic;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author xuchaoxin
 * @Date 2021/1/29 23:08
 * @Version 1.0
 * the detail and improved version(if I update it ) to see:
 * the github repository (search the article):https://github.com/xuchaoxin1375/LearnJava
 */
public class Judge {
    
    
    public static void main(String[] args) {
    
    
/*实例化出三个不同类型(没有继承关系的三个类型的)容器对象(类型具体的)
* 这些类型虽然没有继承关系,但是有共同点,是兄弟类型(可以抽象成Class<T>)
*/
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();

        name.add("icon");
        age.add(18);
        number.add(314);

        getFirstElement(name);
        getFirstElement(age);
        getFirstElement(number);

    }

    /**
     * 打印传入的容器List<T>容器对象中的第一个元素.</T>
     * 参数类型为List<?> 也就是所有List<T>的父类(可以接受任意具体类型T对应的具体类型List<T>类型的实参)
     * @param listT
     */
        public static void getFirstElement(List<?> listT) {
    
    
        System.out.println("firstElement of "+listT.getClass().getName()+" : " + listT.get(0));
    }
    /*test for  <T>*/
//    public static void getData(<T> data) {
    
    
//        System.out.println("data :" + data.get(0));
//    }

//    public static void getData(List<T> data) {
    
    
//        System.out.println("data :" + data.get(0));
//    }
}

类型通配符在使用方面的限制特性:

例子:

在这里插入图片描述

使用类似<? super Integer>通配符作为方法参数时表示:

方法内部可以调用传入Integer引用的方法,例如:obj.setFirst(Integer n);;

方法内部无法调用获取Integer引用的方法(Object除外),例如:Integer n = obj.getFirst()

无限定通配符<?>很少使用,可以用<T>替换,同时它是所有<T>类型的超类。

代码例子

class Pair

package generic;

/**
 * @Author xuchaoxin
 * @Date 2021/1/30 19:31
 * @Version 1.0
 * the detail and improved version(if I update it ) to see:
 * the github repository (search the article):https://github.com/xuchaoxin1375/LearnJava
 */
class Pair<T> {
    
    
    /*T可以是Integer,Double,Decimal,Number,...等具体类型*/
    private T first;
    private T last;
    public Pair(T first, T last) {
    
    
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
    
    
        return first;
    }
    public T getLast() {
    
    
        return last;
    }
    public void setFirst(T first) {
    
    
        this.first = first;
    }
    public void setLast(T last) {
    
    
        this.last = last;
    }
}

class TestExtends

package generic;
/**
 * 泛型有界通配符的限制(举例说明)
 * 主要是指,操作/访问泛型类实例化出来的对象的方法,这些方法中包含extends或super通配符.
 * 对比extends和super通配符
 * 作为(泛型类之外的)方法(记为Method())的参数,<? extends T>类型和<? super T>类型的区别在于:
 *
 * <? extends T>允许Method()调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
 *
 * <? super T>允许Method()调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。

 */

import org.jetbrains.annotations.NotNull;

/**
 * @Author xuchaoxin
 * @Date 2021/1/30 19:31
 * @Version 1.0
 * the detail and improved version(if I update it ) to see:
 * the github repository (search the article):https://github.com/xuchaoxin1375/LearnJava
 */
public class TestExtends {
    
    
    public static void main(String[] args) {
    
    
        /*从泛型类Pair<T>实例化出具体的类型Pair<Integer>类型的对象p(确定而且具体)*/
//        Pair<Integer> p = new Pair<>(123, 456);
        Pair<Number> p=new Pair<>(132,34);
        /*尝试调用泛型方法 static int add(Pair<? extends Number> p)*/
       // int n = add(p);
    }

    /**
     *
     * @param p 传入的参数p可以是Pair<Number>/Pair<Integer>/Pair<double>/..等类型的对象
     *          到底是哪一个类型只有再调用该方法的时候才能够知道(确定下来)
     *          这就意味着在赋值等写操作上面会收到限制:即编写该方法的实现的时候,左值的类型是不确定的
     *          而赋值操作需要保证左值的类型是右值(表达式值)类型的本类或父类
     *          如此以来,你为了赋值就必须保证满足以下条件中的至少一点:
     *              传入的参数总是Pair<Number>(失去了上界通配符泛型的意义,不如直接确定类型,而且实际上还是报错)
     *              右值总是左值(实参p的类型)的子类类型(一般无法实现)
     *          其他的写操作也类似(增加元素/删除元素)
     * @return
     */
    static int add(Pair<? extends Number> p) {
    
    
        Number  first = p.getFirst();
        Number last = p.getLast();
        /*尝试分别修改类型为Number(或其子类,即与实参p同类型的)p.first和p.last成员的值:
        * 这里由于p的类型由传入的实参具体确定,该方法是无法独立确定p的类型,这就导致了
        * 对p.first,p.last的赋值表达式的类型也不能够有方法独立确定的,而应该与p的类型一样(依赖与实参类型)
        * 从反面来看(理解),如果传入的参数p是Double类型的,而传给setFirst的表达式如果是Integer类型的值,就出现类型不符,导致赋值(修改)失败.
        * 比如:
        * p.setFirst(first.intValue() + 100);
        *    */

        return p.getFirst().intValue() + p.getFirst().intValue();
    }
/*对比:*/
    static void add2(@NotNull Pair<?> p){
    
    
        /*如果参数是Pair<?>,返回类型将难以确定
        * generic.Pair<T> public T getFirst()
        * 这里T=?,则:
        *  ? first=p.getFirst();*/
        Object first=p.getFirst();
        Object last=p.getLast();
    }

}

class TestSuper

package generic;

/**
 * @Author xuchaoxin
 * @Date 2021/1/30 20:48
 * @Version 1.0
 * the detail and improved version(if I update it ) to see:
 * the github repository (search the article):https://github.com/xuchaoxin1375/LearnJava
 */
public class TestSuper {
    
    
    public static void main(String[] args) {
    
    
        /*实例化两个不同的泛型类的具体对象对象*/
        Pair<Number> p1 = new Pair<>(12.3, 4.56);
        Pair<Integer> p2 = new Pair<>(123, 456);
        /*尝试执行写的方法*/
        setTwoMemberSame(p1, 100);
        setTwoMemberSame(p2, 200);
        /*执行读的方法*/
        System.out.println(p1.getFirst() + ", " + p1.getLast());
        System.out.println(p2.getFirst() + ", " + p2.getLast());
    }

    static void setTwoMemberSame(Pair<? super Integer> p, Integer n) {
    
    
        p.setFirst(n);
        p.setLast(n);
    }
    static void printGetValues(Pair<? super Integer> p){
    
    
        /*可以获得p的成员值(或者说可以打印出来)
        * 但却无法确定其成员的引用
        * 比如,被作为右值(被读取的值(表达式)p.getFirst()的类型是Integer或其父类),
        * 那么左值(也就是接收右值的引用变量)的类型该如何声明呢?,这就要求保证左值的类型右值类型的本类或父类
        * 而右值的类型无法独立由独立于对象(Pair<? super Integer> 类型的对象)之外的第三方方法来确定
        * 也就难以保证左值是右值的父类.
        * 可见,第三方方法中,对于以下界通配符修饰的泛型实例化出来的对象为参数的方法,可以对参数进行修改(写操作),但是难以获得参数对象的成员的引用(Object 类型的引用变量除外)只能够读到值(打印),但难做他用,因为不知到类型)*/
        /*例外:*/
        Object obj=p.getFirst();
    }

}

猜你喜欢

转载自blog.csdn.net/xuchaoxin1375/article/details/113407877