1.泛型

1.为什么需要泛型:

Java推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。

2.泛型的定义:

Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。

3.泛型的优点:

  • 编译期间检查类型,类型安全,确保不会发生ClassCation异常。
  • 在获取集合里的元素的时候不需要强制类型转换,按集合里指定的类型接收

4.类型:

  • E – Element (在集合中使用,因为集合中存放的是元素)
  • T – Type(表示Java 类,包括基本的类和我们自定义的类)
  • K – Key(表示键,比如Map中的key)
  • V – Value(表示值)
  • N – Number(表示数值类型)
  • ? – (表示不确定的java类型)
  • S、U、V – 2nd、3rd、4th types
import java.util.ArrayList;

public class Demo12 {
    public static void main(String[] args) {
        ArrayList<String> strList = new ArrayList<>();
        strList.add("Java");
        strList.add("Golang");

        for (int i = 0; i < strList.size(); i++) {
            String s = strList.get(i);  //在获取集合里的元素的时候不需要类型转换,按集合里指定的类型接收
            System.out.println(s);
        }
        System.out.println("----------------");
        ArrayList<Integer> intList = new ArrayList<>();
        intList.add(100);
        intList.add(200);

        for (int i = 0; i < intList.size(); i++) {
            Integer integer = intList.get(i);
            System.out.println(integer);
        }

    }
}

运行结果:

Java
Golang
----------------
100
200

2.泛型类

1.泛型类的定义方法:

class 类名称 <泛型标识,泛型标识,···>{
  private 泛型标识 变量名:
  ······
}

2.常用泛型标识:T、E、K、V

3.使用语法:

类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();

JDK1.7以后,后面的<>中的具体的数据类型可以省略不写,也称为菱形语法。

类名<具体的数据类型> 对象名 = new 类名<>();

4.注意事项:

  • 泛型类在创建对象的时候,没有指定类型时将按照Object类型来操作
  • 泛型的类型参数只能是类类型,不能是基本数据类型。
  • 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同的类型
/*<T>  泛型标识---类型实参
       T 创建对象的时候往里制定具体的数据类型
 */
public class Demo01<T>{
    private T key;

    public Demo01(T key) {
        this.key = key;
    }

    public T getKey() {
        return key;
    }

    public void setKey(T key) {
        this.key = key;
    }

    @Override
    public String toString() {
        return "Demo01{" +
                "key=" + key +
                '}';
    }
}
public class Demo02 {
    public static void main(String[] args) {
        Demo01<String> stringDemo01 = new Demo01<>("java");
        String key1 = stringDemo01.getKey();
        System.out.println("key1:"+key1);
        System.out.println("==========");
        Demo01<Integer> integerDemo01 = new Demo01<>(100);
        Integer key2 = integerDemo01.getKey();
        System.out.println("key2:"+key2);
        System.out.println("==========");
        Demo01 objDemo01 = new Demo01("ABC");
        Object key3 = objDemo01.getKey();
        System.out.println("key3:"+key3);
        System.out.println("==========");
        System.out.println(stringDemo01.getClass());
        System.out.println(integerDemo01.getClass());
        System.out.println(stringDemo01.getClass()==integerDemo01.getClass());

    }
}

运行结果:

key1:java
==========
key2:100
==========
key3:ABC
==========
class com.generic.Demo01
class com.generic.Demo01
true

1.案例

模拟抽奖器:

import java.util.ArrayList;
import java.util.Random;
/*
   抽奖器
 */
public class ProductGetter<T> {
    Random random = new Random();
    //奖品
    private T product;

    //奖品池
    ArrayList<T> list = new ArrayList<>();

    //添加奖品
    public void addProduct(T t){
        list.add(t);
    }

    //抽奖
    public T getProduct() {
        T product = list.get(random.nextInt(list.size()));
        return product;
    }
}
public class MainClass {
    public static void main(String[] args) {
        //创建抽奖器对象,指定数据类型
        ProductGetter<String> stringProductGetter = new ProductGetter<>();
        //创建奖品
        String[] strProducts = {"苹果手机","小米手机","空调","冰箱"};
        //遍历,填充奖品
        for (int i = 0; i < strProducts.length; i++) {
            //调用泛型类的addProduct方法
            stringProductGetter.addProduct(strProducts[i]);

        }
        //获取Product,抽奖
        String product1 = stringProductGetter.getProduct();  //泛型类成员方法的调用
        System.out.println("恭喜你,你抽中了:"+product1);
        System.out.println("================");
        ProductGetter<Integer> integerProductGetter = new ProductGetter<>();
        int[] intProducts = {5000,6000,8000,20000};
        for (int i = 0; i < intProducts.length; i++) {
            integerProductGetter.addProduct(intProducts[i]);
        }
        Integer product2 = integerProductGetter.getProduct();
        System.out.println("恭喜你,你抽中的奖金为:"+product2);

    }
}

运行结果:

恭喜你,你抽中了:小米手机
================
恭喜你,你抽中的奖金为:20000

2.泛型类派生子类

1.子类也是泛型类,子类和父类的泛型类型要一致

class ChildGeneric<T> extends Generic<T>
//父类
public class Parent <E>{
    private E value;

    public E getValue() {
        return value;
    }

    public void setValue(E value) {
        this.value = value;
    }
}
//子类
public class ChildFirst<T> extends Parent<T>{
    @Override
    public T getValue() {
        return super.getValue();
    }
}
public class Test01 {
    public static void main(String[] args) {
        ChildFirst<String> stringChildFirst = new ChildFirst<>();
        stringChildFirst.setValue("java");
        String s = stringChildFirst.getValue();
        System.out.println(s);    //java
    }
}

2.子类不是泛型类,父类要明确泛型的数据类型

class ChildGeneric extends Generic<String>
//泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
public class ChildSecond extends Parent<Integer> {
    @Override
    public Integer getValue() {
        return super.getValue();
    }

    @Override
    public void setValue(Integer value) {
        super.setValue(value);
    }
}
public class Test01 {
    public static void main(String[] args) {
        ChildFirst<String> stringChildFirst = new ChildFirst<>();
        stringChildFirst.setValue("java");
        String s = stringChildFirst.getValue();
        System.out.println(s);    //java

        ChildSecond childSecond = new ChildSecond();
        childSecond.setValue(520);
        Integer value = childSecond.getValue();
        System.out.println(value);  //520
        
    }
}

3.泛型接口

1.泛型接口的定义语法:

interface 接口名称 <泛型标识,泛型标识,…> {
	泛型标识 方法名(); 
	.....
}

2.泛型接口的使用

  • 实现类也是泛型类,实现类和接口的泛型类型要一致
public interface Generator<T> {
    T getKey();
}
/**
 * 泛型接口的实现类,是一个泛型类
 * 那么要保证实现接口的泛型类泛型标识,包含泛型接口的泛型标识
 * @param <T>
 * @param <E>
 */
public class Pair<T,E> implements Generator<T>{

    private T key;
    private E value;

    public Pair(T key, E value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public T getKey() {
        return key;
    }

    public E getValue() {
        return value;
    }
}
public class Test02 {
    public static void main(String[] args) {
        Apple apple = new Apple();
        String key = apple.getKey();
        System.out.println(key);

        System.out.println("============");
        Pair<String,Integer> pair = new Pair<>("java",100);
        String key1 = pair.getKey();
        Integer key2 = pair.getValue();
        System.out.println(key1+"="+key2);   //java=100
    }
}
  • 实现类不是泛型类,接口要明确数据类型
//实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型.
public class Apple implements Generator<String>{
    @Override
    public String getKey() {
        return "hello,generator";
    }
}
public class Test02 {
    public static void main(String[] args) {
        Apple apple = new Apple();
        String key = apple.getKey();
        System.out.println(key);  //hello,generator

    }
}

3.泛型方法

1.泛型类与泛型方法的区别:

  • 泛型类,是在实例化类的时候指明泛型的具体类型。(强制要求成员方法返回的泛型必须遵从类的泛型)
  • 泛型方法,是在调用方法的时候指明泛型的具体类型。(调用方法的时候独立指定的)

2.语法:

修饰符 <TE, ...> 返回值类型 方法名(形参列表) { 
   方法体... 
}

3.说明:

  • public与返回值中间< T >非常重要,可以理解为声明此方法为泛型方法。
  • 只有声明了< T >的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
  • < T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。

4.与泛型类的案例的对比:

import java.util.ArrayList;
import java.util.Random;
/*
   抽奖器
 */
public class ProductGetter<T> {
   Random random = new Random();
    //奖品
    private T product;

    //奖品池
    ArrayList<T> list = new ArrayList<>();

    //添加奖品
    public void addProduct(T t){
        list.add(t);
    }

    //抽奖
    public T getProduct() {
        T product = list.get(random.nextInt(list.size()));
        return product;
    }

    /**
     * 定义泛型方法
     * @param list 参数
     * @param <E> 泛型标识,具体类型,由调用方法的时候来指定。
     * @return
     */
    public <E> E getProduct(ArrayList<E> list){
        return list.get(random.nextInt(list.size()));
    }
}
import java.util.ArrayList;

public class Test03 {
    public static void main(String[] args) {
        ProductGetter<Integer> integerProductGetter = new ProductGetter<>();

        int[] intProducts = {5000,6000,8000,20000};
        for (int i = 0; i < intProducts.length; i++) {
            integerProductGetter.addProduct(intProducts[i]);
        }
        //泛型类成员方法的调用
        Integer product = integerProductGetter.getProduct();
        System.out.println(product+"\t"+product.getClass().getSimpleName());
        System.out.println("============");

        ArrayList<String> stringArrayList = new ArrayList<>();
        stringArrayList.add("macbook");
        stringArrayList.add("iphone");
        stringArrayList.add("robot");
        //泛型方法的调用,类型是通过调用方法的时候来指定的。
        String product1 = integerProductGetter.getProduct(stringArrayList);
        System.out.println(product1+"\t"+product1.getClass().getSimpleName());  
        System.out.println("============");

        ArrayList<Integer> integerArrayList = new ArrayList<>();
        integerArrayList.add(10000);
        integerArrayList.add(20000);
        integerArrayList.add(30000);
        Integer product2 = integerProductGetter.getProduct(integerArrayList);
        System.out.println(product2+"\t"+product2.getClass().getSimpleName());  
    }
}

运行结果:

5000	Integer
============
robot	String
===========
30000	Integer

5.静态泛型方法:

普通的成员方法如果采用了类的泛型,那么这个方法将不能被声明为静态的。

 /**
     * 静态的泛型方法,采用多个泛型类型
     * @param t
     * @param e
     * @param k
     * @param <T>
     * @param <E>
     * @param <K>
     */
    public static <T,E,K> void printType(T t, E e, K k) {
        System.out.println(t + "\t" + t.getClass().getSimpleName());
        System.out.println(e + "\t" + e.getClass().getSimpleName());
        System.out.println(k + "\t" + k.getClass().getSimpleName());
    }

调用多个泛型类型的静态泛型方法:

ProductGetter.printType(100,"java",true);

运行结果:

100	Integer
java	String
true	Boolean

6.泛型方法与可变参数(可变参数本质上是数组)

public <E> void print(E... e){
	for (E e1 : e) {     //增强for
		System.out.println(e);
	}
}
    /**
     * 泛型可变参数的定义
     * @param e
     * @param <E>
     */
    public static <E> void print(E... e){
        for (int i = 0; i < e.length; i++) {   //for
            System.out.println(e[i]);
        }
    }

调用:

ProductGetter.print("java","python","golang");

运行结果:

java
python
golang

7.泛型方法总结:

  • 泛型方法能使方法独立于类而产生变化。(最重要的特点)
  • 如果static方法要使用泛型能力,就必须使其成为泛型方法。

4.类型通配符

1.类型通配符的定义:

  • 类型通配符一般是使用”?”代替具体的类型实参。
  • 所以,类型通配符是类型实参,而不是类型形参。

2.类型通配符的上限:

  • 语法:
    类/接口<? extends 实参类型>
    要求该泛型的类型,只能是实参类型,或实参类型的子类类型。

采用上限通配符的集合不能够填充元素。

3.类型通配符的下限

  • 语法:
    类/接口<? super 实参类型>
    要求该泛型的类型,只能是实参类型,或实参类型的父类类型。

采用下限通配符的集合可以填充子类类型,但是不保证元素数据类型的约束要求。

遍历的时候直接为Object类型接收。

  • 使用:

TreeSet的源码:

public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
        }
public class Animal {
    public String name;

    public Animal(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Cat extends Animal{
    public int age;

    public Cat(String name, int age) {
        super(name);
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class MinCat extends Cat{
    public int level;

    public MinCat(String name, int age, int level) {
        super(name, age);
        this.level = level;
    }

    @Override
    public String toString() {
        return "MinCat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", level=" + level +
                '}';
    }
}
import java.util.Comparator;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) {  
        //TreeSet采用的是下限通配符,所以不能是comparator3
        TreeSet<Cat> treeSet = new TreeSet<>(new comparator2());  
        //可以指定一个比较器。
        //按照年龄大小来比较。
        //如果是comparator1将按照name的首字母来比较。
        treeSet.add(new Cat("Tom",6));
        treeSet.add(new Cat("Bob",4));
        treeSet.add(new Cat("Jim",5));
        for (Cat cat : treeSet) {
            System.out.println(cat);
        }

    }
}

//定义三个外部比较器
class comparator1 implements Comparator<Animal>{
    @Override
    public int compare(Animal o1, Animal o2) {
        return o1.name.compareTo(o2.name);
    }
}

class comparator2 implements Comparator<Cat>{
    @Override
    public int compare(Cat o1, Cat o2) {
        return o1.age-o2.age;
    }
}

class comparator3 implements Comparator<MinCat>{
    @Override
    public int compare(MinCat o1, MinCat o2) {
        return o1.level-o2.level;
    }
}

运行结果:

Cat{name='Bob', age=4}
Cat{name='Jim', age=5}
Cat{name='Tom', age=6}

5.类型擦除

1.概念:

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为——类型擦除。

2.分类:

  • 无限制类型擦除

在这里插入图片描述

  • 有限制类型擦除(按照上限类型来擦除,把T转换为上限类型)

在这里插入图片描述

  • 擦除方法中类型定义的参数

在这里插入图片描述

  • 桥接方法

在这里插入图片描述

6.泛型与数组

1.泛型数组的创建:

  • 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
ArrayList<String>[] listArr = new ArrayList<>(5); //会报错
ArrayList<String>[] listArr = new ArrayList[5];

用泛型类型创建数组引用,而对象不能够采用泛型了,采用原始数组赋值给引用listArr,操作的时候就可以用listArr操作。

  • 可以通过java.lang.reflect.Array的**newInstance(Class,int)**创建T[]数组。
import java.lang.reflect.Array;

public class Fruit <T>{
    private T[] array;  //不能够直接new,类型不确定

    //构造方法
    public  Fruit(Class<T> clz,int length){
        //通过Array.newInstance创建泛型数组
        array = (T[])Array.newInstance(clz,length);
    }

    /**
     * 填充数组
     * @param index
     * @param item
     */
    public void put(int index ,T item){
        array[index] = item;
    }

    /**
     * 获取数组元素
     * @param index
     * @return
     */
    public T get(int index){
        return array[index];
    }

    public T[] getArray(){
        return array;
    }
}
import java.util.Arrays;

public class Test02 {
    public static void main(String[] args) {
        Fruit<String> fruit = new Fruit<>(String.class,3);
        fruit.put(0,"西瓜");
        fruit.put(1,"苹果");
        fruit.put(2,"香蕉");

        System.out.println(Arrays.toString(fruit.getArray()));
        String s1 = fruit.get(2);
        System.out.println(s1);

    }
}

运行结果:

[西瓜, 苹果, 香蕉]
香蕉

7.泛型和反射

反射常用的泛型类:

  • Class< T >
  • Constructor< T >
public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
import java.lang.reflect.Constructor;

public class Test03 {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Constructor<Person> constructor = personClass.getConstructor();
        Person person = constructor.newInstance();
    }

}

版权声明:本文为weixin_51609696原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_51609696/article/details/118004700