Java 泛型是什么?有什么用?

版权声明:本文为HCG原创文章,未经博主允许不得转载。请联系[email protected] https://blog.csdn.net/qq_39455116/article/details/87879779

01、泛型是什么?

​ A:泛型其实就是在定义类、接口、方法的时候不局限地指定某一种特定类型,而让类、接口、方法的调用者来决定具体使用哪一种类型的参数。

​ B:比如一个水杯生产的时候不用指定它将来干什么?而是由将来的使用者决定放入什么。

​ C:其实就是一句话:我是一个泛型队列,狗可以站进来,猫也可以站进来,但最好不要既站猫,又站狗!

​ 别让猫狗站在队列里

注:在Java中,经常用T、E、K、V等形式的参数来表示泛型参数。

T:代表一般的任何类。
E:代表 Element 的意思,或者 Exception 异常的意思。
K:代表 Key 的意思。
V:代表 Value 的意思,通常与 K 一起配合使用。

02、不使用泛型,放入猫狗

package fanxing;

import java.util.HashMap;
import java.util.Map;

public class MaoGou {
    class Dog {
    }
    class Cat {
    }
    public static void main(String[] args) {
        MaoGou maoGou = new MaoGou();
        Map map = new HashMap();
        map.put("dog", maoGou.new Dog());
        map.put("cat", maoGou.new Cat());
        Cat cat = (Cat) map.get("dog");
        Dog dog = (Dog) map.get("cat");
        System.out.println(dog + "dog");
        System.out.println(cat + "cat");
    }
}

Exception in thread "main" java.lang.ClassCastException:
fanxing.MaoGou$Dog cannot be cast to fanxing.MaoGou$Cat
编译阶段没有问题,运行时报错,就是放入的是猫,不能取出的是狗
2.1那如何解决上述问题呢?用泛型!!!

直接在map定义的时候指定要放入的对象

package fanxing;
import java.util.HashMap;
import java.util.Map;
public class MaoGou {
    class Dog {
    }
    class Cat {
    }
    public static void main(String[] args) {
        MaoGou maoGou = new MaoGou();
        Map map = new HashMap();
//        map.put("dog", maoGou.new Dog());
//        map.put("cat", maoGou.new Cat());
//        Cat cat = (Cat) map.get("dog");
//        Dog dog = (Dog) map.get("cat");
//        System.out.println(dog + "dog");
//        System.out.println(cat + "cat");
        Map<String, Cat> map2 = new HashMap<>();
        map2.put("cat2", maoGou.new Cat());
        Cat cat2 = map2.get("cat2");
        System.out.println("cat2    " + cat2);
    }

}

03、泛型的存在阶段是编译阶段还是运行阶段?

有人说,Java的泛型做的只是表面功夫——泛型信息存在于编译阶段(狗队在编译时不允许站猫),运行阶段就消失了(运行时的队列里没有猫的信息,连狗的信息也没有)——这种现象被称为“类型擦除”。

package fanxing;
import java.util.HashMap;
import java.util.Map;
public class MaoGou {
    class Dog {
    }
    class Cat {
    }
    public static void main(String[] args) {
        MaoGou maoGou = new MaoGou();
        Map map = new HashMap();
        Map<String, Cat> catMap = new HashMap<>();
        Map<String, Dog> dogMap = new HashMap<>();
        System.out.println(catMap.getClass());
        System.out.println(dogMap.getClass());
    }

}

输出结果:
class java.util.HashMap
class java.util.HashMap
分析:
也就是说,Java代码在运行的时候并不知道catMap的键位上放的是Cat,dogMap的键位上放的是Dog。
	即满足了上述说的《类型擦除》
3.1 那么,试着想一些可怕的事情:

既然运行时泛型的信息被擦除了,而反射机制是在运行时确定类型信息的,那么利用反射机制,是不是就能够在键位为Cat的Map上放一只Dog呢?

package fanxing;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class MaoGou {
    class Dog { }
    class Cat { }
    public static void main(String[] args) {
        MaoGou maoGou = new MaoGou();
        Map map = new HashMap();

        Map<String, Cat> catMap = new HashMap<>();
        try {
            Method method =catMap.getClass().getDeclaredMethod("put",Object.class,Object.class);
            method.invoke(catMap,"dog",maoGou.new Dog());
            System.out.println(catMap);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

输出结果:{dog=fanxing.MaoGou$Dog@3f99bd52}
看到没?我们竟然在键位为Cat的Map上放了一只Dog!

注:Java的设计者在JDK 1.5时才引入了泛型,但为了照顾以前设计上的缺陷,同时兼容非泛型的代码,
不得不做出了一个折中的策略:编译时对泛型要求严格,运行时却把泛型擦除了——要兼容以前的版本,
还要升级扩展新的功能,真的很不容易!
 

04、自定义泛型

package fanxing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class PetHouse<T> {
    private List<T> list;

    public PetHouse() {

    }

    public void add(T item) {
        list.add(item);
    }

    public T get() {
        return list.get(0);
    }

    public static void addTest(List<?> list) {
        Object o = new Object();
        // list.add(o); // 编译报错
        // list.add(1); // 编译报错
        // list.add("ABC"); // 编译报错
        list.add(null);
    }


}

05、泛型通配符

? 通配符类型 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List<?>
       无边界的通配符的主要作用就是让泛型能够接受未知类型的数据. 
<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类
<? super T> 表示类型下界(Java Core中叫超类型限定),
	表示参数化类型是此类型的超类型(父类型),直至Object
注意: 你可以为一个泛型指定上边界或下边界, 但是不能同时指定上下边界.

上界限定符接受 extends 后面类的本身与其子类, 下界限定符接受 super 后面类的本身与其父类。无限定通配符接受任何类。

5.1 ?不能使用add()方法,NULL除外
public static void addTest(List<?> list) {
    Object o = new Object();
    // list.add(o); // 编译报错
    // list.add(1); // 编译报错
    // list.add("ABC"); // 编译报错
    list.add(null);
}

所以“?”声明的集合,不能往此集合中添加元素,所以它只能作为生产者(亦即它只能被迭代),如下:

  public static void main(String[] args) {
        List<?> names = new ArrayList<String>() {
            {
                for (int i = 0; i < 10; i++) {
                    add("A" + i);
                }
            }
        };
        System.out.println(names.toString());
        //  只能以Object迭代元素
        for (Object name : names) {
            System.out.println(name);
        }
    }
5.2 “? extends T”也不能添加元素 只能存指定类型或者其子类
public static void main(String[] args) {
        List<? extends String> names = new ArrayList<String>() {
            {
                for (int i = 0; i < 10; i++) {
                    add("A" + i);
                }
            }
        };
        System.out.println(names.toString());
        // 相比?更能准确定位元素类型
        for (String name : names) {
            System.out.println(name);
        }

    }

​ 因为Integer Double Long都是Number的子类

 List<? extends Number> numbers =new ArrayList<Number>(){
            {
                add(1);
                add(1.2);
                add((long)1);
                
                //add("2"); 报错
            }
        };

猜你喜欢

转载自blog.csdn.net/qq_39455116/article/details/87879779