Java泛型——类泛型、接口泛型、方法泛型的实现及泛型限定与类型变化关系

本文深入讲解Java泛型的概念,包括泛型类、泛型方法、泛型接口的使用及其实现方式,同时探讨了泛型的限定、类型变化关系如协变、逆变等高级主题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:

泛型即编写的代码可以被很多不同的对象使用,通常有泛型类(ArrayLis、HashSet、HashMap)泛型方法(Collection.binarySearch,Array.sort)、泛型接口(List、Iterator),

Iterator是一个轻量级的迭代器,iterator()要求容器返回一个Iterator,而hasNext()用来检测是否还含有元素:

        ArrayList<String> stringList =new ArrayList<String>();
        stringList.add("string1");
        stringList.add("string2");
        stringList.add("string3");

        ArrayList<Integer> intList =new ArrayList<Integer>();
        intList.add(1);
        intList.add(2);
        intList.add(3);

        Iterator<String> iterString =stringList.iterator();
        while(iterString.hasNext()){
            System.out.println(iterString.next());
        }

        Iterator<Integer> iterInt =intList.iterator();
        while (iterInt.hasNext()){
            System.out.println(iterInt.next());
        }

 泛型类

泛型类是指具有泛型变量的类,在类名后用<T>表示引入类型,如果有多个字母的话代表引入多种类型<T,U>,具体用哪个字母表示并没有明确的规定,只不过我们都会遵从默认的规则。引入的类型可以用来修饰成员变量、局部变量、参数、返回值。

public class Interval <T> {
    private T lower;
    private T upper;
    public Interval(T lower,T upper){
        this.upper =upper;
        this.lower =lower;
    }
    public T getLower() {
        return lower;
    }
}

 对泛型类调用,在我们的菱形语法中,可以根据左侧的值退出右侧的类型,所以右侧尖括号中的类型可以省略不写:

public static void main(String[] args) {

        Interval<Integer> v1 =new Interval<Integer>(1,2);
        Interval<Integer> v2 =new Interval<>(3,4);

        System.out.println("v1:"+v1.getLower()+","+v1.getUpper()+"; v2:"+v2.getLower()+","+v2.getUpper());

        System.out.println("经过高低值交换后的情况  "+"v1:"+getReverse(v1).getLower()+","+getReverse(v1).getUpper()+"; v2:"+getReverse(v2).getLower()+","+getReverse(v2).getUpper());
        
    }
    public static <T> Interval<T> getReverse(Interval<T> interval){
        return new Interval<>(interval.getUpper(),interval.getLower());
    }

 泛型方法

泛型方法中的<T>的位置在修饰符之后,返回类型之前,在上面代码中的getReveerse()中,以第一个<T>代表的就是整个方法的一个泛型,Interval<T>代表的是返回值类型。如果要是我们的方法中不止有一个类型U、T,而返回值中只有一个类型T,那么我们就应该写成:

public static <U,T> Interval<T> getReverse(Interval<T> interval,U temp){
        System.out.println(temp.toString());
        return new Interval<>(interval.getUpper(),interval.getLower());
    }

 泛型方法调用的具体示例:

public class ArrayUtil {
    public static <T> T getMiddle(T... a){
        return a[a.length/2];
    }
}

//    如果菱形语法可以推断出数据的类型,那么方法前的<>可以省略不写
    String s1 =ArrayUtil.<String>getMiddle("abc","def","ghi");
    Integer i1 = ArrayUtil.getMiddle(1,2,3);
//    空值是被允许的
    String s2 =ArrayUtil.<String>getMiddle("abc","def",null);
//     如果没有指定方法的类型,而传进去的类型又不一致,JDK会先寻找共同类,再进行转型,会存在报错
//    i2报错,i3不报错;牵扯到类型转化
    Integer i2 =ArrayUtil.getMiddle(1,2.5f,3L);
    Object i3 =ArrayUtil.getMiddle(1,2.5f,3L);

 泛型接口

泛型接口的写法与泛型类是相似的,我们在实现类中指定具体的实现类型,在泛型接口的实例中,应当注意这句代码,以及他背后的实现思路: Calculator<Interval<Integer>> c =new IntervalCalculator();

public interface Calculator<T> {
    Interval<Integer> add(Interval<Integer> operand1, Interval<Integer> operand2);
}

public class Interval <T> {
    private T lower;
    private T upper;
    public Interval(T lower,T upper){
        this.upper =upper;
        this.lower =lower;
    }
    public T getLower() {
        return lower;
    }

    public T getUpper() {
        return upper;
    }
}

public class IntervalCalculator implements Calculator<Interval<Integer>>{

    public static void main(String[] args) {
//        注意这种表达形式
        Calculator<Interval<Integer>> c =new IntervalCalculator();
        Interval<Integer> operand1 = new Interval<>(1,2);
        Interval<Integer> operand2 = new Interval<>(3,4);
        System.out.println("lower:"+c.add(operand1,operand2).getLower()+",upper:"+c.add(operand1,operand2).getUpper());
    }

    @Override
    public Interval<Integer> add(Interval<Integer> operand1, Interval<Integer> operand2) {
        Integer lower =operand1.getLower()+operand2.getLower();
        Integer upper =operand1.getUpper()+operand2.getUpper();
        Interval<Integer> operandTemp =new Interval<>(lower,upper);
        return operandTemp;
    }
}

 自定义的泛型设计中,泛型类是整个类都被泛化,这其中包括变量和方法。泛型接口泛化的是子类的方法,泛型方法是指方法被泛化,其中包括返回值与参数。

 泛型的限定

我们先假设类A集成与类B,且A、B均为泛型类/接口,类C继承与类D,但是C、D都是普通的类/接口。

在上面假设的基础上A<D>继承与B<D>,但是A<C>,A<D>之间是没有任何关系的,即A、B可能存在集成关系,他们都属于泛型类或者接口。但是作为泛型类型的C、D放在泛型类或接口之后他们之间是没有任何关系的。我了使得他们可能存在一些关系,我们可以通过A<T extend C>来指定这个泛型中的类型都是C的子类或C,也就是说A所能接受的范围应该是C的子类或C;A< T super C>来表明泛型中的类型都应该是C的父类或者C,也就是说A能就收的都是C的父类或者C。我们管第一种的叫做上线界定符,第二种叫做下限界定符。

泛型中有一个PECS(Producer Extend,Consumer Super)原则:要从泛型中读取数据但是不能写入,这时候我们可以使用 ? extends 通配符,泛型是生产者,往外输出东西;往泛型中放入数据但是不能输出,这时候我们使用 ?super 通配符,泛型是消费者,往内增加东西。如果即想写入又想读出,那么就不能使用通配符。

原始类型的<T>与无限定通配符的<?>的不同之处在于前者在编译的时候类型就是固定的,而后者是不固定的。

类型变化关系

类型变化关系分为协变、逆变、不变、双变:

Java的数组是协变的,原始泛型是不变的,但是可以通过通配符的形式类实现协变或者逆变。< ? extends A>支持协变,< ? super B>支持逆变

在JDK1.4重写方法的参数和返回值是要求一样的,但是到了JDK 1.5+ 重写方法只要求参数是一样的,返回值是可以协变的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值