【Java学习记录-10】泛型

一、 泛型

1 概述

泛型(Generic)是一种编程概念,它是指一种可以用于多种不同的数据类型的程序设计方式。它是一种类或方法,可以接受不同类型的参数,以达到代码复用的目的。

2 核心思想

在 Java、C# 等编程语言中,泛型是一种重要的编程机制,可以适用于各种数据结构,如List、Set、Map等等。泛型的核心思想是参数化类型,即定义类或方法时不指定具体的数据类型,而是使用一个参数来代替数据类型,具体的数据类型在使用时再传递进来。例如,Java中的List接口可以通过泛型来实现一个通用的列表类,同时支持存储各种数据类型的对象。

3 优点

  • 泛型使得代码变得更为灵活和复用性更强,可以使用相同的类或方法来处理不同类型的数据,而无需针对不同的数据类型重复编写相似的代码。

  • 使用泛型的优点包括增加代码的可读性、减少代码重复、提高代码复用性和可维护性等。

  • 把运行时的问题,提到了编译期间

  • 泛型也可以大大减少类型转换的使用,从而避免了类型转换错误的发生,并提高代码的性能。

4 格式

Java中泛型的定义格式如下:

class/interface 类型名称<泛型参数列表> {
    在此可以使用泛型参数列表中的参数
}

其中,类型名称代表定义的类或接口名称,泛型参数列表用尖括号 < > 括起来,泛型参数之间用逗号隔开。

在类或接口中可以使用泛型参数,以限定方法参数、方法返回值及成员变量的数据类型,例如:

class MyClass<T> {
    private T data;
    public void setData(T data){
        this.data = data;
    }
    public T getData(){
        return data;
    }
}

上面的例子中,MyClass 是一个使用泛型的类,T 是泛型参数,可以用于限定 setData() 方法的参数类型和 getData() 方法的返回值类型,也可以用于成员变量 data 的数据类型。

使用泛型的好处是,可以在编译时检查类型安全性,防止类型转换错误的发生,并且代码具有更好的灵活性和可重用性。

二、泛型类

1 概述

泛型类(Generic Class)是一种定义了类型参数的类,它可以适用于不同类型的数据。

2 定义格式

修饰符 class 类名<T>{
    // 成员变量和方法
}

其中,T 是类型参数,可以在类中使用,表示一种占位符类型,相当于在定义类时定义了一种类型,可以在该类内使用。常见的如T、E 、K、V等形式的参数常用于表示泛型

泛型类可以使用在任何数据类型上,并且可以用于所有的方法返回类型和参数类型。例如,Java中的List接口、Map接口等都是泛型类。通过泛型,可以灵活地实现多种类型的List和Map,从而提高代码的可重用性和灵活性。

3 举例

下面是一个在Java中的泛型类的例子:

public class Box<T> {
   private T value;
 
   public Box(T value) {
      this.value = value;
   }
 
   public T getValue() {
      return value;
   }
}

以上是一个简单的泛型类 Box,它的类型参数是 T。Box 类用于存储一个对象,并提供了一个返回该对象的方法 getValue()。

我们可以使用泛型类 Box 来存储不同类型的对象,例如:

Box<Integer> intBox = new Box<Integer>(10);
Box<String> strBox = new Box<String>("Hello");

上面的代码定义了两个不同的 Box 对象,一个用于存储整型,一个用于存储字符串。在定义时,需要指定 T 的实际类型。可以通过调用 getValue() 方法来获取存储在 Box 对象中的值。由于泛型的作用,这两个Box对象可以在处理数据时实现代码的复用。

三、 泛型方法

1 概述

泛型方法 (Generic Method) 可以独立于泛型类而存在,它可以在普通类中定义,也可以在泛型类中定义。

在 Java 中,泛型方法可在以下位置定义:

  • 在泛型类中
  • 在非泛型类中
  • 接口中

2 格式

一个泛型方法可以定义在一个普通类中,其格式如下:

修饰符 <T, V, ...> 返回类型 方法名 (参数列表) {
    方法体
}

其中,泛型参数列表 T、V 等是可变的,方法体中的代码可以使用这些泛型参数。

3 举例

public class MyUtil {
    public static <T> void printArray(T[] array) {
        for(T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

上面的代码中,MyUtil 类中定义了一个泛型方法 printArray(),它可以处理任何类型的数组参数,并输出数组的元素值。

在使用泛型方法时,需要显式地指定泛型参数类型。例如:

Integer[] intArray = {1, 2, 3, 4, 5};
MyUtil.<Integer>printArray(intArray);

上面的代码中,我们调用了 MyUtil 类中的 printArray() 泛型方法处理一个整型数组,并显式地指定了泛型参数类型为 Integer。

四、泛型接口

1 概述

泛型接口 (Generic Interface) 是一种定义了类型参数的接口,它和泛型类一样可以适用于不同类型的数据。

2 格式

泛型接口的定义格式为:

interface 接口名<T> {
    // 方法和静态常量
}

其中,T 是类型参数,可以在接口内使用,表示一种占位符类型,和泛型类的类型参数类似。

3 场景

  • 泛型接口可以和泛型类一样使用在任何数据类型上,并且可以用于所有的方法返回类型和参数类型。
    例如,Java中的Map.Entry接口就是一个泛型接口,它定义了一个键值对,其中键和值都可以是不同类型的任何对象类型。
  • 泛型接口的使用和泛型类类似,可以通过类型参数来指定具体的数据类型。
  • 可以通过在实现时指定不同的类型参数来定义多种类型的接口,提高接口的灵活性。

4 优点

使用泛型接口的好处是和泛型类类似,提高了代码的安全性和灵活性,使得代码更具通用性和可重用性。

5 举例

下面是一个在Java中的泛型接口的例子:

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

以上代码定义了一个泛型接口 Pair,它有两个类型参数 K 和 V,用于表示两个元素的类型。Pair 接口包含了两个方法 getKey() 和 getValue(),用于获取键和值的值。

我们可以在任何类中实现 Pair 接口,例如:

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;
    }
}

上面的代码定义了一个类 OrderedPair,它实现了 Pair 接口,并根据实际需要定义了键和值的类型。在 OrderedPair 类中,我们需要实现 Pair 接口中所有的方法,以保证 OrderedPair 实例能够正确地获取其键和值的值。

通过泛型接口,我们可以灵活地定义不同类型的键值对,而且可以保证类型安全,提高了代码的重用性和可读性。

五、类型通配符

1 概述

在Java泛型中,类型通配符(Wildcard)是一种使用?通配符(?)来表示未知类型的占位符,具体类型在使用时会由编译器根据上下文推断。通过使用类型通配符,我们可以编写更加灵活的泛型代码,使其能够处理多种类型的数据。

2 分类

类型通配符可以用于定义泛型方法、泛型类和泛型接口中的参数、返回值、成员变量和数组。类型通配符主要有以下三种形式:

  • ?:表示未知的类型参数,它可以匹配任何数据类型;
  • ? extends 类型:表示类型参数是类型的子类型(包括自身),可以用于限制泛型类型的上界,如 List<? extends Number> 表示 List 中元素的类型必须是 Number 类型或其子类;
  • ? super 类型:表示类型参数是类型的超类型(包括自身),可以用于限制泛型类型的下界,如 List<? super Integer> 表示 List 中元素的类型必须是 Integer 类型或其父类。

3 举例

import java.util.List;
 
public class MyUtil {
    public static double sum(List<?> list) {
        double sum = 0;
        for (Object obj : list) {
            if (obj instanceof Number) {
                sum += ((Number) obj).doubleValue();
            }
        }
        return sum;
    }
 
    public static void copy(List<? extends Number> src, List<? super Number> dest) {
        for (Number num : src) {
            dest.add(num);
        }
    }
}

上面的代码中,使用了 List<?>List<? extends Number> 类型通配符来表示未知的泛型类型和泛型类型的上界。其中 sum() 方法可以计算 List 中所有数值的和,而 copy() 方法可以将一个 List 中的元素复制到另一个 List 中,通过使用类型通配符,这两个方法可以适用于任何数据类型。

六、可变参数

1 概述

可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了

2 格式

修饰符 返回值类型 方法名(数据类型… 变量名){}
eg: public static int sum(int... a) {}

注意事项:

  • 这里的变量a,其实是一个数组
  • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后

3 举例

下面举一个Java中,使用泛型可变参数实现数组转换为列表的例子:

import java.util.ArrayList;
import java.util.List;

public class ArrayToList {
    public static <T> List<T> arrayToList(T... array) {
        List<T> list = new ArrayList<>();
        for (T element : array) {
            list.add(element);
        }
        return list;
    }

    public static void main(String[] args) {
        String[] array = {"apple", "banana", "orange"};
        List<String> list = arrayToList(array);
        System.out.println(list); // 输出:[apple, banana, orange]
    }
}

在上面的例子中,我们定义了一个泛型方法 arrayToList,这个方法使用了可变参数来接收一个数组,并且将数组转换为一个列表。方法的泛型类型为 <T>。在方法内部,我们通过遍历数组,将数组元素添加到 List 中,最终返回 List 对象。

在 main 方法中,我们声明一个 String 类型的数组,将其传递给 arrayToList 方法,并得到一个 String 类型的 List 对象,然后使用输出语句输出这个 List 对象:[apple, banana, orange]。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值