欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java基础=>分析Java1.5的新特性-泛型

程序员文章站 2024-03-17 18:07:04
...

一.什么是泛型?

  • 泛型是Java 1.5的新特性,泛型的本质是参数化类型,也就是将所操作的数据类型被指定为一个参数

  • 泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

  • Java中的泛型是伪泛型,只在编译期生效,运行期自动进行泛型擦除将泛型替换为实际上传入的类型

二.泛型产生的背景以及如何定义泛型

泛型产生的背景

  1. 集合中保存数据时,编译期间可以保存任何类型的数据,导致取值时转换出错;
  2. 每次取值都需要强制转换,代码冗余繁琐;

泛型的定义

  • 泛型的本质是一个参数化的类型(在程序编码中一些包含参数的类),也就是说所操作的数据类型被指定为一个参数。

参数有两种表现

  1. 体现在方法的一个参数变量上,定义方法时的参数叫形参,定义方法时传递的参数叫实参
  2. 体现在或者方法或者接口上,用泛型的格式展现出来的叫参数类型;

泛型的好处

  • 保证传入参数的类型安全,会在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的
  • 消除了强制类型转换 使得代码可读性好,减少了很多出错的机会

三.泛型使用场景

泛型类

class <T> A {}这样的形式表示,里面的 方法 和 成员变量 都可以用T来表示类型。
语法: 类名<A——Z任何一个字母>{}

  例如:
 class Container<K,V>{
    private K key;
    private V value;
    
    public K getKey() {
        return key;
    }

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

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }
}  

例如:
class Box<T> {
    private T object;

    public void set(T object) { this.object = object; }
    public T get() { return object; }
}

创建一个Box对象,不带泛型参数,发现获取对象的时候需要强制转

Box box = new Box();
box.set(new Apple());
Apple apple = (Apple) box.get();

创建一个Box对象,带泛型参数,获取对象的时候就不需要强制转换

Box<Apple> box = new Box<Apple>();
box.set(new Apple());
Apple apple = box.get();
泛型接口

泛型类实现泛型接口时可以选择注入实际类型或者是继续使用泛型。

  1. 一个类实现了带泛型的接口时,如果在实现类中具体化泛型(传递了泛型参数);
  2. 如果接口的方法中使用了接口中的类型,那么重写该方法中的泛型形参全部要替换为该泛型实参;
  3. 接口中泛型也可以在实现类中向实现类传递而不采用泛型实参来替换;
例如:
interface IUserService<T>{
    T queryUser();
}

//通过类去实现这个泛型接口的时候指定泛型T的具体类型  (任意类型都可以)。
class  MaleUserServiceImpl implements  IUserService {
    //指定为Stirng类型
    @Override
    public String queryUser() {
        return null;
    }
}

class  MemaleUserServiceImpl implements  IUserService {
    //指定为User类型
    @Override
    public User queryUser() {
        return new User();
    }
}
泛型方法

可以自带泛型如 void <E> E go();

  1. 不能在所有的静态方法、静态初块等所有静态内容中使用泛型的类型参数;
  2. 泛型方法的定义和普通方法定义不同的地方在于需要在修饰符和返回类型之间加一个泛型类型参数的声明,表明在这个方法作用域中谁才是泛型类型参数;
  3. class A<T> { ... }中T的作用域就是整个A
  4. public <T> func(...) { ... }中T的作用域就是方法func
  5. 泛型方法的类型参数可以指定上限,类型上限必须在类型参数声明的地方定义上限,不能在方法参数中定义上限。规定了上限就只能在规定范围内指定类型实参,超出这个范围就会直接编译报错。<T extends X> void func(List<T> list){ ... }
例如:
class Test<E> {
    public <T> T getObject(Class<T> clazz) throws IllegalAccessException, InstantiationException {
        return clazz.newInstance();
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Test test = new Test();
        Test<T> test2 = new Test<T>();
        Test<String> test3 = new Test<String>();

        //需要强转类型
        User user = (User) test.getObject(User.class);

        //不需要强转类型
        User user2 = test2.getObject(User.class);

        //指定类
        String user3 = test2.getObject(String.class);
    }
}


泛型通配符
  1. 泛型可以使用?通配符进行泛化 Object<?>可以接受任何类型
    也可以使用<? extends Number> <? super Integer>这种方式进行上下边界的限制。
  2. 通配符是用“?”代替具体的类型参数。
    • 例如 集合类接口List< ?>在逻辑上是List、List 等所有List<具体类型实参>的父类;
  3. List < ? extends T> :
    • 表示该通配符所代表的类型是T类型的子类;(接收T类型或者T的子类型。)
  4. List < ? super T>:
    • 表示该通配符所代表的类型是T类型的父类; (接收T类型或者T的父类型。)
    
泛型通配符T,E,K,V区别

使用大写字母A,B,C,D…X,Y,Z定义的,就都是泛型,把T换成A也一样,这里T只是名字上的意义而已

  • ? 表示不确定的java类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element
List,List,List<?>区别
ArrayList<T> al=new ArrayList<T>(); //指定集合元素只能是T类型
ArrayList<?> al=new ArrayList<?>(); //集合元素可以是任意类型,这种没有意义,一般是方法中,只是为了说明用法
ArrayList<? extends E> al=new ArrayList<? extends E>(); //指定集合元素只能是T类型
泛型的限定:
? extends E:接收E类型或者E的子类型。
? super E:接收E类型或者E的父类型
  1. Object和T不同点在于: Object是一个实打实的类,并没有泛指谁,而T可以泛指Object,比方
    public void printList(List<T> list){}方法中可以传入List<Object> list类型参数,也可以传入List<String> list类型参数,
    但是public void printList(List<Object> list){}就只可以传入List<Object> list类型参数,因为Object类型并没有泛指谁,是一个确定的类型
  2. ?和T区别是: ?是一个不确定类,?和T都表示不确定的类型 ,但如果是T的话,函数里面可以对T进行操作,比方 T car = getCar(),而不能用? car = getCar()。
Class和Class<?>
  • 使用Class和Class<?>多发生在反射场景下,先看看如果我们不使用泛型,反射创建一个类是什么样的。
People people = (People) Class.forName("com.lyang.demo.fanxing.People").newInstance();
//需要强转,如果反射的类型不是People类,就会报 java.lang.ClassCastException错误。
  • 使用Class泛型后,不用强转了
public class Test {
    public static <T> T createInstance(Class<T> clazz) throws IllegalAccessException, InstantiationException {
        return clazz.newInstance();
    }

    public static void main(String[] args)  throws IllegalAccessException, InstantiationException  {
            Fruit fruit= createInstance(Fruit .class);
            People people= createInstance(People.class);
    }
}
  • 那Class和Class<?>有什么区别呢?
    • Class在实例化的时候,T要替换成具体类
    • Class<?>它是个通配泛型,?可以代表任何类型,主要用于声明时的限制情况

例如可以声明一个
public Class<?> clazz;
但是你不能声明一个
public Class<T> clazz;
因为T需要指定类型
所以当,不知道声明什么类型的Class的时候可以定义一个Class<?>,Class<?>可以用于参数类型定义,方法返回值定义等