【Java难点】泛型

一、什么是泛型

1.背景

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

2.概念

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

@Test
public void test(){
  ArrayList arrayList = new ArrayList();
  arrayList.add(1);
  arrayList.add(true);
  arrayList.add("aa");
  for (int i = 0; i < arrayList.size(); i++) {
    Object o = arrayList.get(i);
    String str=(String)o;
    System.out.println(str);
  }
}

如果不定义泛型,在编译器不会发生异常,但在运行期会出现类型转换的异常,在项目上线后会出现严重问题。

image-20231228224208401

3.好处
  • 类型安全(编译器会检查数据类型是否符合泛型)

  • 消除了强制类型的转换

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

二、泛型类、接口

1.泛型类

(1) 泛型类的定义语法

class 类名称 <泛型标识1,泛型标识2,...>{
	private 泛型标识1 变量名1;
  private 泛型标识2 变量名2;
  ...
}

可以将泛型标识理解为类型的形参,即将类型参数化。这个形参的实参只能是java中的类型,如String类,Integer类,自定义的Animal类等…

例:

/**
 * 泛型类的定义
 * @param <T> T为泛型标识-可以理解为类型形参
 *           创建对象的时候,将T指定为具体的数据类型
 */
public class Generic<T>{
  //T: 由外部使用该类的时候来指定。
    private T key;
  	public Generic(){}
    public Generic(T key) {
        this.key = key;
    }
    public T getKey() {
        return key;
    }
    public void setKey(T key) {
        this.key = key;
    }
}

(2) 泛型类的使用语法

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

​ Java1.7以后,后面的<>中的具体的数据类型可以省略不写

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

例:

public static void main(String[] args) {
  //泛型类在创建对象的对候,来指定操作的具体数据类型。
  Generic<String> strGeneric = new Generic<>("abc");
  String key = strGeneric.getKey();
  System.out.println(key);

  Generic<Integer> intGeneric = new Generic<>(1);
  Integer key1 = intGeneric.getKey();
  System.out.println(key1);
  //泛型类在创建对象的时候,没有指定泛型类型,将按照Object类型来操作
  Generic generic = new Generic(100);
  Object key2 = generic.getKey();
  System.out.println(key2);
  //同一泛型类,根据不同的数据类型创建的对象,本质上是同一类型,泛型擦除之后 Generic <String>和Generic<Integer>都是Generic类。
  System.out.println(intGeneric.getClass() == strGeneric.getClass());
}
2.泛型类注意事项
  • 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object

  • 泛型的类型参数只能是类类型(Integer,String,Animal等),不能是基本数据类型(short、int、double等)

  • 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型(intGeneric.getClass() == strGeneric.getClass()结果为true)

3.从泛型类派生子类
  • 子类也是泛型类,子类和父类的泛型类型要一致
    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;
    }
}

例1

/**
 * 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
 * @param <T>
 */
class ChildFirst1<T> extends Parent<T> { //符合
    @Override
    public T getValue() {
        return super.getValue();
    }
}

例2

/**
 * 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
 * @param <T>
 */
class ChildFirst2<E> extends Parent<E> { //符合
    @Override
    public E getValue() {
        return super.getValue();
    }
}

例3

/**
 * 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
 * 如果子类定义了多个泛型,必须存在其中一个泛型和父类中的相同
 * @param <T>
 */
class ChildFirst3<T,E,K> extends Parent<T> { //符合
    @Override
    public T getValue() {
        return super.getValue();
    }
}

例4

/**
 * 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。
 * @param <T>
 */
class ChildFirst3<T> extends Parent<E> { //不符合
    @Override
    public T getValue() {
        return super.getValue();
    }
}

因为创建子类对象时,会先自动创建父类对象,会将子类对象中指定的具体数据类型传递给父类,如果父类和子类定义的泛型标识不一致,父类则无法得到子类传递的具体数据类型

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

class ChildGeneric extends Generic<String>

例1

/**
 * 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
 * 父类没有指定具体的数据类型,则泛型类型默认为Object,参考ArrayList
 */
class ChildSecond1 extends Parent{ //符合
    @Override
    public Object getValue() {
        return super.getValue();
    }
}

例2

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

例3

/**
 * 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
 */
class ChildSecond3 extends Parent<T>{ //不符合
    @Override
    public T getValue() {
        return super.getValue();
    }
}

因为创建子类对象时,会先自动创建父类对象,会将子类对象中指定的具体数据类型传递给父类,但是如果子类不是泛型类,则不会给父类传递任何具体的数据类型,父类也就无法为泛型赋予具体的数据类型,所以父类一定要明确数据类型

4.泛型接口

(1)泛型接口的定义语法

interface 接口名称 <泛型标识1,泛型标识2,...> {
	泛型标识1 方法名1(); 
  泛型标识2 方法名2(); 
	...
}
5.从泛型接口派生实现类
  • 实现类也是泛型类,实现类和接口的泛型类型要一致

class ChildGeneric<T> implements Generic<T>

/**
 * 泛型接口
 * @param <T>
 */
public interface Generator<T>{
    T getKey();
}

例1

/**
 * 实现类也是泛型类,实现类和接口的泛型类型要一致
 * @param <T>
 */
class Pair1<T> implements Generator<T> { //符合
    @Override
    public T getKey() {
        return null;
    }
}

例2

/**
 * 实现类也是泛型类,实现类和接口的泛型类型要一致
 * @param <T>
 */
class Pair2<E> implements Generator<E> { //符合
    @Override
    public E getKey() {
        return null;
    }
}

例3

/**
 * 实现类也是泛型类,实现类和接口的泛型类型要一致
 * 如果实现类定义了多个泛型,必须存在其中一个泛型和接口中的相同
 * @param <T>
 * @param <E>
 * @param <K>
 */
class Pair3<K,V> implements Generator<K> { //符合
    private K key;
    private V value;
  	@Override
    public K getKey() {
        return key;
    }
    public V getValue() {
        return value;
    }
}

例4

/**
 * 实现类也是泛型类,实现类和接口的泛型类型要一致
 * @param <E>
 */
class Pair4<E> implements Generator<T> { //不符合
    @Override
    public E getKey() {
        return null;
    }
}
  • 实现类不是泛型类,接口要明确数据类型

class ChildGeneric implements Generic<String>

例1

/**
 * 实现类不是泛型类,接口要明确数据类型
 * 接口没有指定具体的数据类型,则泛型类型默认为Object,参考List
 */
class Apple1 implements Generator{ //符合
    @Override
    public Object getKey() {
        return null;
    }
}

例2

/**
 * 实现类不是泛型类,接口要明确数据类型
 */
class Apple2 implements Generator<String>{ //符合
    @Override
    public String getKey() {
        return null;
    }
}

例3

/**
 * 实现类不是泛型类,接口要明确数据类型
 */
class Apple3 implements Generator<T>{ //符合
    @Override
    public T getKey() {
        return null;
    }
}

三、泛型方法

  • 泛型类,是在实例化类的时候指明泛型的具体类型。
  • 泛型方法,是在调用方法的时候指明泛型的具体类型。
1.定义语法
修饰符 <T,E,...> 返回值类型 方法名(形参类型 形参) { 
  方法体... 
}
  • 修饰符与返回值中间非常重要,可以理解为声明此方法为泛型方法。

  • 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。

  • <T>表明该方法将使用泛型类型T,此时才可以在方法中(返回值类型、形参列表、方法体)使用泛型类型T

  • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。

class TestMethod<E>{
  	private E value;
    /**
     * 1.定义泛型方法
     * 泛型方法能使方法独立于类而产生变化,泛型方法中的泛型标识独立存在,不必和类中的泛型标识保持一致
     * @param list 形参
     * @param <E> 泛型标识,具体类型,由调用方法的时候来指定
     * @return
     */
    public <E> E getProduct(ArrayList<E> list){
        return list==null ? null : list.get(0);
    }
  
  	/**
     * 方法中使用泛型,但是非泛型方法,此类方法中的泛型标识必须和类中定义的泛型标识一致
     * @return
     */
    public E getValue() {
        return value;
    }
  
    /**
     * 2.定义泛型方法
     * 定义静态的泛型方法,采用多个泛型类型
     * @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());
    }
  
  	/**
     * 方法中使用泛型,但是非泛型方法,不能定义成静态方法。
     * 如果静态方法要使用泛型能力,就必须使其成为泛型方法
     * @param e
     */
    public static void printInfo(E e){ //编译出错
        System.out.println(e);
    }
    
    /**
     * 3.定义泛型方法
     * 定义参数为可变参数的泛型方法
     * @param t 形参
     * @param <T> 泛型标识,具体类型,由调用方法的时候来指定
     * @return
     */
    public <T> void print(T... t){
        for (T subT : t) {
            System.out.println(subT);
        }
    }
}
2.使用语法
public static void main(String[] args) {
  TestMethod<Integer> testMethod = new TestMethod<>();
  ArrayList<String> strList = new ArrayList<>();
  strList.add("笔记本电脑");
  strList.add("苹果手机");
  //1.调用泛型方法,类型是通过调用方法的时候来指定
  String product = testMethod.getProduct(strList);
  System.out.println(product);
  System.out.println("-------------------------");
  
  //2.调用静态泛型方法
  TestMethod.printType("平板电脑",999,true);
  System.out.println("-------------------------");
  
  //3.调用泛型方法
  testMethod.print(100,true,"华为手机",strList);
}
笔记本电脑
-------------------------
平板电脑	String
999	Integer
true	Boolean
-------------------------
100
true
华为手机
[笔记本电脑, 苹果手机]
  • 泛型方法能使方法独立于类而产生变化(最重要的特点),所以能用泛型方法时尽量不要用泛型类。

  • 如果static方法要使用泛型能力,就必须使其成为泛型方法

  • 调用参数为可变参数的泛型方法时,可以传入任意个任意类型的实参

四、类型通配符

1.什么是类型通配符
  • 类型通配符一般是使用"?"代替具体的类型实参,可以理解为一个占位符。

  • 类型通配符是类型实参,而不是类型形参。

例:使用类型通配符的原因

public class MyTest {
    public static void showBox(Box<Number> box){
        Number first = box.getFirst();
        System.out.println(first);
    }
  
    public static void main(String[] args) {
        Box<Number> box1 = new Box<Number>();
        box1.setFirst(100);
        showBox(box1);

        Box<Integer> box2 = new Box<Integer>();
        box2.setFirst(100);
        showBox(box2); //编译出错,因为showBox只能接收泛型为Number的Box对象。虽然Integer继承Number类。
    }
}
class Box<E>{
    private E first;
    public E getFirst() {
        return first;
    }
    public void setFirst(E first) {
        this.first = first;
    }
}

解决办法

public class MyTest {
    public static void showBox(Box<?> box){ //使用通配符
        Object first = box.getFirst();
        System.out.println(first);
    }

    public static void main(String[] args) {
        Box<Number> box1 = new Box<>();
        box1.setFirst(100);
        showBox(box1);

        Box<Integer> box2 = new Box<>();
        box2.setFirst(100);
        showBox(box2); //编译通过,因为通配符?代表任意类型
    }
}
class Box<E>{
    private E first;
    public E getFirst() {
        return first;
    }
    public void setFirst(E first) {
        this.first = first;
    }
}
2.类型通配符的上限
  • 语法:类/接口<? extends 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型。

public class MyTest {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<>();
        ArrayList<Cat> cats = new ArrayList<>();
        ArrayList<MiniCat> miniCats = new ArrayList<>();
//        showAnimal(animals); 编译报错,因为只能传Cat或Cat的子类
        showAnimal(cats); //编译通过
        showAnimal(miniCats); //编译通过
    }
    /**
     * 泛型上限通配符:传递的集合类型只能是Cat或Cat的子类类型
     * @param list
     */
    public static void showAnimal(ArrayList<? extends Cat> list){
        /*
           使用泛型通配符上限的集合不允许填充元素。
           原因:不能确定传进来的list存的是Cat还是Cat的子类,如果传进来的list存的是MiniCat类型的元素,那么向list中填充Cat类型的数据就是错误的。所以干脆不允许填充任何类型的元素。
        */
//        list.add(new Cat()); 编译报错
//        list.add(new MiniCat()); 编译报错
        for (int i = 0; i < list.size(); i++) {
            Cat cat = list.get(i);
            System.out.println(cat);
        }
    }
}
class Animal{}
class Cat extends Animal{}
class MiniCat extends Cat{}

注意使用泛型通配符上限的集合不允许填充元素。

3.类型通配符的下限
  • 语法:类/接口<? super 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的父类类型。

public class MyTest {
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList<>();
        ArrayList<Cat> cats = new ArrayList<>();
        ArrayList<MiniCat> miniCats = new ArrayList<>();

        showAnimal(animals); //编译通过
        showAnimal(cats); //编译通过
//        showAnimal(miniCats); //编译错误,因为只能传Cat或Cat的父类
    }
    /**
     * 类型通配符下限:要求集合中元素的类型只能是Cat或Cat的父类类型
     * @param list
     */
    public static void showAnimal(ArrayList<? super Cat> list){
        /*
           使用泛型通配符下限的集合允许填充元素,但是只能填充Cat或Cat的子类,不能填充Cat的父类或与Cat无关的类
           原因:list在存储的时候会将MiniCat向上转型为Cat,也就是list.add((Cat)new MiniCat());,而向上转型是隐式的,所以add其子类也就是add其本身,所以是可以的。通配符?可以匹配Cat的所有父类,这个具体的父类类型在编译期间是无法预知的,只有程序运行起来才能具体知道类型,假设传进来的元素类型为Cat,此时向集合中添加Cat的父类就会出现异常,因为父类对象不能指向子类引用,需要强制转型。
        */
//        list.add(new Object());编译报错
//        list.add(new Animal());编译报错
        list.add(new Cat()); //编译通过
        list.add(new MiniCat()); //编译通过
        for (int i = 0; i < list.size(); i++) {
            Object object = list.get(i);
            System.out.println(object);
        }
    }
}
class Animal{}
class Cat extends Animal{}
class MiniCat extends Cat{}

注意使用泛型通配符下限的集合允许填充元素,但是只能填充Cat或Cat的子类,不能填充Cat的父类或与Cat无关的类

实例:

import java.util.Comparator;
import java.util.TreeSet;

public class MyTest {
    public static void main(String[] args) {
        TreeSet<Cat> cats = new TreeSet<>(new Comparator2());//TreeSet可以根据Comparator2()中定义比较方法,将传进来的元素从小到大进行排序
        cats.add(new Cat("jerry",20));
        cats.add(new Cat("amy",22));
        cats.add(new Cat("frank",35));
        cats.add(new Cat("jim",15));
        for (Cat cat : cats) {
            System.out.println(cat);
        }
        System.out.println("-------------------");
        /*
         public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));}
         TreeSet的构造方法中可以传入比较器,比较器中泛型下限为Cat
         */
        TreeSet<Cat> cats1 = new TreeSet<>(new Comparator1());
        cats1.add(new Cat("jerry",20));
        cats1.add(new Cat("amy",22));
        cats1.add(new Cat("frank",35));
        cats1.add(new Cat("jim",15));
        for (Cat cat : cats1) {
            System.out.println(cat);
        }
        System.out.println("-------------------");
//        TreeSet<Cat> cats2 = new TreeSet<>(new Comparator3());编译报错,因为比较器中泛型下限是Cat,而Comparator3中的泛型为MiniCat,MiniCat是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<MiniCat>{
    @Override
    public int compare(MiniCat o1, MiniCat o2) {
        return o1.level-o2.level;
    }
}
class Animal{
    public String name;
    public Animal(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                '}';
    }
}
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 +
                '}';
    }
}
class MiniCat extends Cat{
    public int level;
    public MiniCat(String name, int age, int level) {
        super(name, age);
        this.level = level;
    }
    @Override
    public String toString() {
        return "MiniCat{" +
                "level=" + level +
                '}';
    }
}

泛型下限总结创建Cat对象时首先会创建Cat的父类Animal对象,而public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));}中使用了泛型下限。TreeSet<Cat> cats = new TreeSet<>(new Comparator2())TreeSet<Cat> cats = new TreeSet<>(new Comparator1())都是合法的。所以创建子类对象前先创建父类对象的原则和泛型下限是非常契合的。

五、类型擦除

1.概念

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

public class MyTest {
    public static void main(String[] args) {
        ArrayList<Integer> integers = new ArrayList<>();
        ArrayList<String> strings = new ArrayList<>();
        System.out.println(integers.getClass().getSimpleName());//ArrayList
        System.out.println(strings.getClass().getSimpleName());//ArrayList
        System.out.println(integers.getClass()==strings.getClass());//true
    }
}
2.分类
  • 无限制类型擦除

image-20240103210308333

import java.lang.reflect.Field;
public class MyTest {
    public static void main(String[] args) {
        Erasure<Integer> erasure = new Erasure<>();
//        利用反射,获取Erasure类的字节码文件的Class类对象
        Class<? extends Erasure> erasureClass = erasure.getClass();
//        获取所有的成员变量
        Field[] declaredFields = erasureClass.getDeclaredFields();
        for (Field field : declaredFields) {
//            打印成员变量的名称和类型
            System.out.println(field.getName()+":"+field.getType().getSimpleName());//key:Object
        }
    }
}
class Erasure<T>{
    private T key;
    public T getKey() {
        return key;
    }
    public void setKey(T key) {
        this.key = key;
    }
}
  • 有限制的类型擦除

image-20240103210453644

import java.lang.reflect.Field;
public class MyTest {
    public static void main(String[] args) {
        Erasure<Integer> erasure = new Erasure<>();
//        利用反射,获取Erasure类的字节码文件的Class类对象
        Class<? extends Erasure> erasureClass = erasure.getClass();
//        获取所有的成员变量
        Field[] declaredFields = erasureClass.getDeclaredFields();
        for (Field field : declaredFields) {
//            打印成员变量的名称和类型
            System.out.println(field.getName()+":"+field.getType().getSimpleName());//key:Number
        }
    }
}
class Erasure<T extends Number>{
    private T key;
    public T getKey() {
        return key;
    }
    public void setKey(T key) {
        this.key = key;
    }
}
  • 擦除方法中类型定义的参数

image-20240103210758219

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
public class MyTest {
    public static void main(String[] args) {
        Erasure<Integer> erasure = new Erasure();
//        利用反射,获取Erasure类的字节码文件的Class类对象
        Class<? extends Erasure> erasureClass = erasure.getClass();
//        获取所有的成员方法
        Method[] declaredMethods = erasureClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
//            打印成员方法的名称和返回值类型
            System.out.println(method.getName()+":"+method.getReturnType().getSimpleName());
        }
    }
}
class Erasure<T extends Number>{
    private T key;
    public T getKey() {
        return key;
    }
    public void setKey(T key) {
        this.key = key;
    }
    public <T extends List> T show(T t){
        return t;
    }
}
输出结果:
getKey:Number
show:List
setKey:void
  • 桥接方法

image-20240103212007018

import java.lang.reflect.Method;
public class MyTest {
    public static void main(String[] args) {
        Class<Info> infoClass = Info.class;
        Method[] infoClassDeclaredMethods = infoClass.getDeclaredMethods();
        for (Method method : infoClassDeclaredMethods) {
            System.out.println(method.getName()+":"+method.getReturnType().getSimpleName());
        }
        System.out.println("--------------------");
        Class<InfoImpl> infoImplClass = InfoImpl.class;
        Method[] declaredMethods = infoImplClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method.getName()+":"+method.getReturnType().getSimpleName());
        }
    }
}
interface Info<T>{
    T info(T var);
}
class InfoImpl implements Info<Integer>{
    @Override
    public Integer info(Integer var) {
        return var;
    }
}
输出结果:
info:Object
--------------------
info:Integer
info:Object

六、泛型与数组

1.泛型数组的创建
  • 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
public class MyTest {
    public static void main(String[] args) {
        ArrayList[] list = new ArrayList[5]; //非泛型的ArrayList数组,编译通过
//        ArrayList<String>[] arrayLists1 = new ArrayList<String>[5]; //编译错误
        ArrayList<String>[] arrayLists2 = new ArrayList[5]; //编译通过
        arrayLists2[0].add("a");//编译通过
//        arrayLists2[0].add(1);//编译报错,只能接受String类型
        ArrayList<String>[] arrayLists3; //编译通过
    }
}
  • 可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[]数组

使用Array.newInstance()方法创建数组

import java.lang.reflect.Array;
public class Fruit<T> {
    private T[] array;

    /**
     * 利用构造方法创建泛型数组
     */
    public Fruit(Class<T> clz, int length){
        //通过Array.newInstance创建泛型数组
        array = (T[]) Array.newInstance(clz, length);
    }
    /**
     * 填充数组
     */
    public void put(int index,T item){
        array[index]=item;
    }
    /**
     * 获取数组元素
     */
    public T get(int index){
        return array[index];
    }
    /**
     * 获取数组
     */
    public T[] getArray(){
        return array;
    }
}

使用数组

public class MyTest {
    public static void main(String[] args) {
        Fruit<String> stringFruit = new Fruit<String>(String.class,3);
        stringFruit.put(0,"苹果");
        stringFruit.put(1,"西瓜");
        stringFruit.put(2,"香蕉");
        System.out.println(Arrays.toString(stringFruit.getArray()));
        System.out.println(stringFruit.get(0));
    }
}

输出结果

[苹果, 西瓜, 香蕉]
苹果
2.总结

尽量使用泛型集合来代替泛型数组。

七、泛型和反射

1.反射常用的泛型类
  • Class<T>
  • Constructor<T>
import java.lang.reflect.Constructor;
public class MyTest {
    public static void main(String[] args) throws Exception {
        //使用Class<T>泛型类
        Class<Person> personClass = Person.class;
        Constructor<Person> constructor = personClass.getConstructor();
        Person person = personClass.newInstance();//使用Class<T>泛型类,创建出来的对象类型是Person,不需要手动强转为Person类型

        //不使用Class<T>泛型类
        Class personClass1 = Person.class;
        Constructor constructor1 = personClass1.getConstructor();
        Object o = constructor1.newInstance(); //不使用Class<T>泛型类,创建出来的对象类型是Object,需要手动强转为Person类型
    }
}
class Person {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值