第17课-泛型

第17课:泛型

本课目标:理解泛型的概念和作用,掌握泛型类、泛型方法、泛型接口的使用,了解通配符和类型擦除。


一、概念讲解

1.1 为什么需要泛型

没有泛型的问题:

// 需要为每种类型写重复代码
public class IntBox {
    private int value;
    public void set(int value) { this.value = value; }
    public int get() { return value; }
}

public class StringBox {
    private String value;
    public void set(String value) { this.value = value; }
    public String get() { return value; }
}

使用泛型:

// 一个类适用于所有类型
public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

1.2 泛型的优势

泛型的优势:

1. 类型安全:编译时检查类型
   Box<Integer> box = new Box<>();
   box.set(10);      // OK
   box.set("hello"); // 编译错误!

2. 消除强制类型转换
   // 无泛型:Object obj = list.get(0); String s = (String) obj;
   // 有泛型:String s = list.get(0);

3. 代码复用:一份代码适用多种类型

二、语法格式

2.1 泛型类

// 单个类型参数
public class Box<T> {
    private T content;
    public void set(T content) { this.content = content; }
    public T get() { return content; }
}

// 多个类型参数
public class Pair<K, V> {
    private K key;
    private V value;
    // 构造器、getter、setter
}

// 使用
Box<String> stringBox = new Box<>();
Box<Integer> intBox = new Box<>();
Pair<String, Integer> pair = new Pair<>("age", 25);

2.2 泛型方法

// 类型参数放在返回值前面
public class Utils {
    public static <T> T getFirst(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        return array[0];
    }

    // 多个类型参数
    public static <K, V> Map<K, V> zip(K[] keys, V[] values) {
        Map<K, V> map = new HashMap<>();
        for (int i = 0; i < keys.length && i < values.length; i++) {
            map.put(keys[i], values[i]);
        }
        return map;
    }
}

// 使用(类型推断)
String first = Utils.getFirst(new String[]{"a", "b"});
Integer num = Utils.getFirst(new Integer[]{1, 2, 3});

2.3 泛型接口

// 定义泛型接口
public interface Repository<T> {
    void save(T entity);
    T findById(int id);
    List<T> findAll();
}

// 实现时指定具体类型
public class UserRepository implements Repository<User> {
    @Override
    public void save(User entity) { /* ... */ }
    @Override
    public User findById(int id) { /* ... */ }
    @Override
    public List<User> findAll() { /* ... */ }
}

// 实现时保留泛型
public class GenericRepository<T> implements Repository<T> {
    @Override
    public void save(T entity) { /* ... */ }
    @Override
    public T findById(int id) { /* ... */ }
    @Override
    public List<T> findAll() { /* ... */ }
}

2.4 通配符

// 无界通配符:接受任何类型
public void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

// 上界通配符:接受Number及其子类
public double sum(List<? extends Number> list) {
    double total = 0;
    for (Number n : list) {
        total += n.doubleValue();
    }
    return total;
}

// 下界通配符:接受Integer及其父类
public void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

三、代码案例

3.1 泛型Box容器

public class Box<T> {

    private T content;
    private boolean empty = true;

    public Box() {
    }

    public Box(T content) {
        this.content = content;
        this.empty = false;
    }

    public void set(T content) {
        this.content = content;
        this.empty = false;
    }

    public T get() {
        if (empty) {
            throw new IllegalStateException("Box为空");
        }
        return content;
    }

    public boolean isEmpty() {
        return empty;
    }

    public void clear() {
        this.content = null;
        this.empty = true;
    }

    @Override
    public String toString() {
        if (empty) {
            return "Box{空}";
        }
        return "Box{" + content + "}";
    }

    public static void main(String[] args) {
        System.out.println("=== 泛型Box演示 ===");

        // String Box
        Box<String> stringBox = new Box<>("Hello");
        System.out.println("String Box: " + stringBox);
        System.out.println("内容: " + stringBox.get());

        // Integer Box
        Box<Integer> intBox = new Box<>(100);
        System.out.println("Integer Box: " + intBox);
        System.out.println("内容: " + intBox.get());

        // 空Box
        Box<Double> doubleBox = new Box<>();
        System.out.println("Double Box: " + doubleBox);
        System.out.println("是否为空: " + doubleBox.isEmpty());
    }
}

▶ 运行结果:

=== 泛型Box演示 ===
String Box: Box{Hello}
内容: Hello
Integer Box: Box{100}
内容: 100
Double Box: Box{空}
是否为空: true

3.2 泛型Stack

import java.util.EmptyStackException;

public class GenericStack<E> {

    private Object[] elements;
    private int size;
    private static final int DEFAULT_CAPACITY = 10;

    @SuppressWarnings("unchecked")
    public GenericStack() {
        elements = new Object[DEFAULT_CAPACITY];
        size = 0;
    }

    public void push(E element) {
        ensureCapacity();
        elements[size++] = element;
    }

    @SuppressWarnings("unchecked")
    public E pop() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        E element = (E) elements[--size];
        elements[size] = null;
        return element;
    }

    @SuppressWarnings("unchecked")
    public E peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }
        return (E) elements[size - 1];
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public int size() {
        return size;
    }

    private void ensureCapacity() {
        if (size == elements.length) {
            Object[] newArray = new Object[elements.length * 2];
            System.arraycopy(elements, 0, newArray, 0, size);
            elements = newArray;
        }
    }

    public static void main(String[] args) {
        System.out.println("=== 泛型Stack演示 ===");

        GenericStack<String> stack = new GenericStack<>();
        stack.push("Java");
        stack.push("Python");
        stack.push("C++");

        System.out.println("栈大小: " + stack.size());
        System.out.println("栈顶: " + stack.peek());
        System.out.println("弹出: " + stack.pop());
        System.out.println("弹出: " + stack.pop());
        System.out.println("栈大小: " + stack.size());
    }
}

▶ 运行结果:

=== 泛型Stack演示 ===
栈大小: 3
栈顶: C++
弹出: C++
弹出: Python
栈大小: 1

3.3 泛型Pair和工具类

import java.util.*;

public class GenericUtils {

    // 泛型Pair
    public static class Pair<A, B> {
        private final A first;
        private final B second;

        public Pair(A first, B second) {
            this.first = first;
            this.second = second;
        }

        public A getFirst() { return first; }
        public B getSecond() { return second; }

        @Override
        public String toString() {
            return "(" + first + ", " + second + ")";
        }
    }

    // 泛型方法:交换数组元素
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    // 泛型方法:查找最大值
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) >= 0 ? a : b;
    }

    // 泛型方法:过滤列表
    public static <T> List<T> filter(List<T> list, java.util.function.Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T item : list) {
            if (predicate.test(item)) {
                result.add(item);
            }
        }
        return result;
    }

    // 泛型方法:创建不可变Pair
    public static <A, B> Pair<A, B> of(A first, B second) {
        return new Pair<>(first, second);
    }

    public static void main(String[] args) {
        System.out.println("=== 泛型工具类演示 ===");

        // Pair使用
        Pair<String, Integer> pair = new Pair<>("年龄", 25);
        System.out.println("Pair: " + pair);
        System.out.println("Key: " + pair.getFirst());
        System.out.println("Value: " + pair.getSecond());

        // 交换数组
        Integer[] nums = {1, 2, 3, 4, 5};
        swap(nums, 0, 4);
        System.out.println("交换后: " + Arrays.toString(nums));

        // 比较最大值
        System.out.println("max(10, 20) = " + max(10, 20));
        System.out.println("max(\"Java\", \"Python\") = " + max("Java", "Python"));

        // 过滤列表
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> evens = filter(numbers, n -> n % 2 == 0);
        System.out.println("偶数: " + evens);

        // 静态工厂方法
        Pair<String, Boolean> result = of("成功", true);
        System.out.println("结果: " + result);
    }
}

▶ 运行结果:

=== 泛型工具类演示 ===
Pair: (年龄, 25)
Key: 年龄
Value: 25
交换后: [5, 2, 3, 4, 1]
max(10, 20) = 20
max("Java", "Python") = Python
偶数: [2, 4, 6, 8, 10]
结果: (成功, true)

四、常见错误

4.1 原始类型(Raw Types)

import java.util.*;

public class RawTypeError {
    public static void main(String[] args) {
        // 原始类型:没有类型参数
        List list = new ArrayList();  // 原始类型,编译器会警告
        list.add("Hello");
        list.add(123);  // 可以添加任何类型

        // 错误:需要强制类型转换,可能ClassCastException
        String s = (String) list.get(1);  // 运行时异常!

        // 正确:使用泛型
        List<String> safeList = new ArrayList<>();
        safeList.add("Hello");
        // safeList.add(123);  // 编译错误,类型安全
    }
}

▶ 运行结果:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

4.2 泛型数组问题

import java.util.*;

public class GenericArrayError {
    public static void main(String[] args) {
        // 错误:不能创建泛型数组
        // T[] array = new T[10];  // 编译错误

        // 正确方式1:使用Object数组
        @SuppressWarnings("unchecked")
        T[] array1 = (T[]) new Object[10];

        // 正确方式2:使用ArrayList代替数组
        List<T> list = new ArrayList<>();

        // 注意:数组有协变性,泛型没有
        Object[] objects = new String[10];
        objects[0] = "Hello";  // OK
        // objects[1] = 123;   // ArrayStoreException

        // List<String> strings = new ArrayList<Object>();  // 编译错误
    }

    static class T {}
}

4.3 通配符误用

import java.util.*;

public class WildcardError {
    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1, 2, 3);
        List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);

        // 上界通配符:可以读取,不能写入
        List<? extends Number> upperBound = integers;
        Number n = upperBound.get(0);  // OK
        // upperBound.add(1);  // 编译错误!不能写入

        // 下界通配符:可以写入,读取只能是Object
        List<? super Integer> lowerBound = new ArrayList<Number>();
        lowerBound.add(1);  // OK
        lowerBound.add(2);  // OK
        Object obj = lowerBound.get(0);  // 只能读取为Object

        // PECS原则:Producer Extends, Consumer Super
        // 如果集合是生产者(提供数据),用 extends
        // 如果集合是消费者(接收数据),用 super
    }
}

五、课后练习

  1. 泛型容器:实现一个泛型的Pair类,支持两个不同类型的操作。

  2. 泛型排序:编写泛型方法,对任意Comparable类型的数组进行排序。

  3. 泛型Map包装:实现一个泛型的MapWrapper类,提供put、get、remove等方法。

  4. 通配符练习:编写方法,接受List<? extends Number>,返回其中的最大值。

  5. 类型擦除实验:编写程序,验证泛型在运行时被擦除(通过反射获取泛型信息)。


六、本课小结

概念要点
泛型类class Box<T>,类型参数在类名后
泛型方法<T> T method(),类型参数在返回值前
泛型接口interface Repo<T>,实现时指定或保留泛型
无界通配符<?>,接受任何类型
上界通配符<? extends T>,接受T及其子类,只读
下界通配符<? super T>,接受T及其父类,可写
类型擦除运行时泛型信息被擦除,变成Object或上界类型

关键规则:

  • 泛型不能用于基本类型(int用Integer代替)
  • 不能创建泛型数组(用ArrayList代替)
  • PECS原则:Producer Extends, Consumer Super
  • 运行时泛型信息被擦除,不能用instanceof检查泛型类型

本课程持续更新中,欢迎关注!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值