再次认识java泛型

一、什么是泛型

定义类、接口、方法时;同时声明了一个或多个类型变量(如:<E>),称为泛型类,泛型接口,泛型方法,它们统统称为泛型。

<E>这个写法,表示定义了一个泛型类型,定义好之后我们就可以在后面去使用这个泛型类型。

public class ArrayList<E>{
......
}

泛型的本质,是把具体的数据类型作为参数传给类型变量。

泛型的作用:提供了在编译阶段所能操作的数据类型,并自动进行检查的能力。这样可以避免强制类型转换,及可能出现的异常。

例子

package org.example.general;

import java.util.ArrayList;

public class GenExample1 {
    public static void main(String[] args) {
     // ArrayList 本身是一个泛型类,在实例化的时候没有指定泛型类型,那么默认是Object类型 ArrayList arr
= new ArrayList(); arr.add("abc"); arr.add(123); arr.add(true); for(int i = 0;i < arr.size();i++){ String temp = (String) arr.get(i); System.out.println(temp); } } }

运行结果:

abc
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at org.example.general.GenExample1.main(GenExample1.java:13)

可以看到获取第二个数据的时候,报了类型转换异常。

我们优化一下代码,给ArrayList定义变量的时候加上泛型标识,如下:

 可以发现,在编译阶段就已经报错了。因此后面我们添加元素的时候,就只能添加String类型,这样就规范了值的传入,也就能避免后面的类型转换异常了。

 二、泛型类

定义泛型类

修饰符  class 类名 <类型变量,类型变量,...> {
......
}

注意,类型变量建议用大写英文字母,常用的有:E,T,K,V等。

例如,当我们定义类的时候,类中有变量我们不确定它的类型,我们就可以定义泛型类,如下:

package org.example.general;
public class Student<T,K> {   // 有几个类型不确定,就写几个大写字母,这里的T、K表示定义了两个泛型类型为T、K
    private T name;     // 在这里使用泛型类型 T
    private K age;      // 在这里使用泛型类型 K

    public Student(){};
    public  Student(T name,K age){
        this.name = name;
        this.age = age;
    }
    public T getName() {
        return name;
    }
    public void setName(T name) {
        this.name = name;
    }
    public K getAge() {
        return age;
    }
    public void setAge(K age) {
        this.age = age;
    }
}

在我们创建对象的时候,来确定泛型类的数据类型,如下:

package org.example.general;
public class GenExample2 {
    public static void main(String[] args) {
        Student<String,Integer> stu1 = new Student<>("小明",22);
        System.out.println(stu1.getName());
    }
}

再看例子二:

package org.example.general;

public class GenExample3{
    public static void main(String[] args) {
        People<Hdance> per1 = new People<>();
        per1.setHobby(new Hdance());
        per1.getHobby();
        People<Hsing> per2 = new People<>();
        per2.setHobby(new Hsing());
        per2.getHobby();
    }
}
class People<T> {
    private T hobby;

    public void setHobby(T hobby) {
        this.hobby = hobby;
    }

    public void getHobby(){
        System.out.println(hobby.toString());
    }
}
class Hsing{
    @Override
    public String toString() {
        return "唱歌";
    }
}
class Hdance{
    @Override
    public String toString() {
        return "跳舞";
    }
}

三、泛型接口

定义泛型接口

修饰符 interface 接口名<类型变量,类型变量...>{
......
}

什么时候用到了泛型接口:接口当中,方法的参数类型或方法的返回值类型确定不了,这个时候就可以使用泛型接口。

例如该场景:

系统需要处理学生和老师的数据
需要提供两个功能:
1、保存对象数据
2、根据名称查询数据

代码如下:

定义学生类:

package org.example.generalinterface;
public class Student {
}

定义老师类:

package org.example.generalinterface;
public class Teacher {
}

定义操作类:

package org.example.generalinterface;

public interface Operator<E> {
    public abstract void saveData(E obj);
    public abstract E getData();
}

// 接口中的泛型在什么时候被确定
// 方式一:实现类实现泛型接口,没有指定具体的泛型类型,那么默认是object类型
class OperatorImpl1 implements Operator{
    @Override
    public void saveData(Object obj) {

    }
    @Override
    public Object getData() {
        return null;
    }
}

// 方式二:实现类实现接口时,直接指定接口中泛型的类型(使用最多的方式)
class OperatorImpl2 implements  Operator<Teacher>{
    @Override
    public void saveData(Teacher obj) {

    }
    @Override
    public Teacher getData() {
        return null;
    }
}

// 方式三:实现类实现了泛型接口,泛型没有指定,那么泛型在实现类实例化的时候被确定
class OperatorImpl3<E> implements Operator<E>{
    @Override
    public void saveData(E obj) {

    }
    @Override
    public E getData() {
        return null;
    }
}

四、泛型方法

 定义泛型方法

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

例如

public static <T> void test(T t){

}

在调用方法的时候确定泛型类型。

什么时候使用泛型方法:当我们定义方法的时候参数类型或返回值类型不确定的时候,就可以使用泛型方法,当我们调用泛型方法的时候来确定泛型方法。例如:

ackage org.example.generalMethod;

public class GenExample1 {
    public static void main(String[] args) {
        GenExample1.show("abc");  // 输出string
        GenExample1.show(123);  // 输出integer

    }
// 第一个<T> 表示我们定义了一个泛型类型,在show后面的T是使用泛型类型。
public static <T> void show(T t){ if(t instanceof String){ System.out.println("string"); } else if (t instanceof Integer) { System.out.println("interger"); } } }

通配符

?号,可以在使用泛型的时候代表一切类型,E T K V 是在定义泛型的时候使用。这个通配符一般会结合集合去用。

package org.example.generalMethod;

import java.util.ArrayList;

public class GenExample2 {
    public static void main(String[] args) {
        ArrayList<String> str = new ArrayList<>();
        ArrayList<Integer> inte = new ArrayList<>();
        
        method(str);
        method(inte);
    }
  // 通配符不需要定义泛型,就可以使用泛型
public static void method(ArrayList<?> arr){ } }

泛型的上下限

泛型上限: ? extends Car,?能接收的必须是Car或者他的子类

泛型下限: ? super Car,?能接收的必须是Car或者其父类。

 可以看到超过上限,编译的时候就报错了。

posted @ 2024-11-05 23:20  远洪  阅读(5)  评论(0编辑  收藏  举报