JAVA泛型基础

本文介绍了Java泛型在Java5中的引入,重点讲解了泛型的使用场景、类型安全优势、基本用法(包括泛型类、接口和方法)、类型通配符和类型擦除的概念。同时讨论了泛型的局限性及如何克服这些限制以提高代码质量和复用性。
摘要由CSDN通过智能技术生成

Java泛型(Generics)是在Java 5中引入的一种代码编写特性,它使得类、接口和方法可以对类型(类和接口)参数化。泛型的引入极大地提高了代码的复用性、类型安全性以及可读性。

为什么使用泛型?

  • 类型安全:泛型提供编译时类型检查,减少了在运行时出现ClassCastException的可能性。
  • 消除类型转换:使用泛型,不再需要手动类型转换。
  • 泛型算法:可以编写独立于类型的代码,比如各种通用的算法。

泛型的基本用法

  • 泛型类:使用一个或多个类型参数定义的类。
  • 泛型接口:使用一个或多个类型参数定义的接口。
  • 泛型方法:使用一个或多个类型参数定义的方法。

泛型类

一个泛型类的定义包括类名后跟一个类型参数的列表,放在尖括号<>之内。

public class Box<T> {
    private T t; // T stands for "Type"

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }
}

// 使用泛型类
Box<Integer> integerBox = new Box<>();
integerBox.set(10); // 自动装箱成Integer类型
Integer someInteger = integerBox.get(); // 不需要类型转换

在上述例子中,Box是一个泛型类,它有一个类型参数T。创建Box实例时指定实际的类型参数Integer

泛型接口

泛型接口的定义与泛型类类似。

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

// 实现泛型接口的类
public class OrderedPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;

    public OrderedPair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

// 使用泛型接口
Pair<String, Integer> p = new OrderedPair<>("Even", 8);

在这个例子中,Pair是一个泛型接口,它有两个类型参数KV

泛型方法

泛型方法可以在类、接口和枚举内部定义,也可以定义在普通类的方法中。泛型方法可以是静态的,也可以是非静态的。

public class Util {
    // 泛型方法
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

// 使用泛型方法
Pair<Integer, String> p1 = new OrderedPair<>(1, "apple");
Pair<Integer, String> p2 = new OrderedPair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

上述代码中,compare是一个静态泛型方法,它接受两个Pair类型的参数,并返回一个布尔值。

类型通配符

在使用泛型时,有时不需要具体的类型参数,而是希望接受任何类型参数化的对象。这时可以使用类型通配符(Wildcard)?

  • 无限制通配符? 表示任何类型。
  • 有限制通配符? extends Type 表示Type或其子类型;? super Type 表示Type或其父类型。
public void processElements(List<? extends Number> list) {
    for (Number element : list) {
        // ...
    }
}

List<Integer> intList = Arrays.asList(1, 2, 3);
processElements(intList); // 正确:Integer是Number的子类

在上述例子中,processElements方法使用了extends通配符来接受任何Number的子类型的List。因此,我们可以传递一个Integer列表,因为IntegerNumber的子类。

类型擦除

Java泛型是使用类型擦除来实现的,这意味着在编译时,所有的泛型类型参数都会被替换为它们的边界或Object。因此,泛型类型参数在运行时并不保留类型信息。这也是为什么上述的泛型方法processElements在运行时只知道列表中是Number或其子类,而不知道具体是Integer还是Double

泛型和继承

泛型类之间的继承关系并不像普通类那样直接。即使类A是类B的子类,Box<A>并不是Box<B>的子类。

Box<Number> numberBox = new Box<>();
Box<Integer> integerBox = new Box<>();
numberBox = integerBox; // 编译错误

要允许泛型的类型参数之间有继承关系,可以使用通配符:

Box<? extends Number> numberBox = new Box<>();
Box<Integer> integerBox = new Box<>();
numberBox = integerBox; // 正确

泛型的限制

尽管泛型增加了代码的复用性和类型安全性,它们也有一些限制:

  • 由于类型擦除,不能实例化类型参数:new T()
  • 不能声明静态字段为类型参数:static T field
  • 不能使用基本数据类型作为类型参数,如Box<int>
  • 不能创建类型参数数组:new T[10]
  • 泛型类的实例化类型在运行时无法确定。

总结

泛型是Java中的一个复杂主题,可以使代码更加通用,类型更加安全。理解和合理使用泛型可以提高代码质量,并减少运行时错误。在熟悉基础用法后,可以探索更高级的主题,如泛型的继承、泛型通配符的高级应用、泛型反射等。通过实践,你将能够更好地理解泛型的强大功能以及如何在不同情况下使用它们。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员爱学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值