Java中的泛型介绍

Java中的泛型介绍

概述

Java 泛型(generics)是 JDK 5 中引入的, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

泛型的好处是

在编译的时候检查类型安全;

避免了强制类型转换运行时异常;

同一个类可以操作多种类型数据,提高代码复用。

Java 中泛型标记符

在 Java 中,泛型标记符用于表示类型参数。下面是一些常用的泛型标记符及其含义:

  1. E:代表元素(Element)类型,通常在集合中使用,如 List<E>。
  2. K:代表键(Key)的类型,通常在 Map 中使用,如 Map<K, V>。
  3. V:代表值(Value)的类型,通常在 Map 中使用,如 Map<K, V>。
  4. T:代表任意类型(Type),通常在方法中使用,如 public <T> T method(T obj)。
  5. N:代表数字类型,如 Number 类型。
  6. R:代表返回类型,如 public <T> R method(T obj)。

这些泛型标记符只是一种约定,并没有强制规定必须使用它们,你可以使用任何可以作为标识符的字符来表示类型参数。但是,为了代码的可读性和可维护性,建议使用这些标记符。

为什么使用泛型?

如果要实现不同类型的加法,每种类型都需要重载一个add方法。使用泛型适用于多种数据类型执行相同的代码,示例源码如下:

public class NeedGeneric1 {

    private static int add(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    private static float add(float a, float b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    private static double add(double a, double b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    //使用泛型
    private static <T extends Number> double add(T a, T b) {
        System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
        return a.doubleValue() + b.doubleValue();
    }

    public static void main(String[] args) {
        NeedGeneric1.add(1, 2); //1+2=3
        NeedGeneric1.add(1f, 2f); //1.0+2.0=3.0
        NeedGeneric1.add(1d, 2d); //1.0+2.0=3.0
        
        NeedGeneric1.add(Integer.valueOf(1), Integer.valueOf(2)); //1+2=3.0
        NeedGeneric1.add(Float.valueOf(1), Float.valueOf(2)); //1.0+2.0=3.0
        NeedGeneric1.add(Double.valueOf(1), Double.valueOf(2)); //1.0+2.0=3.0
    }
}

Java泛型的应用

下面举几个例子来说明Java泛型的应用。

1.泛型类

定义一个泛型类:public class GenericClass<T>{},例如:

//泛型类例子
public class GenericClass<T> {
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public static void main(String[] args) {
        GenericClass<String> genericClass=new GenericClass<>();
        genericClass.setData("Generic Class");
        System.out.println(genericClass.getData()); //Generic Class
    }
}

2.泛型方法

定义一个泛型方法: private static<T> TgenericAdd(T a, T b) {},例如:

//泛型方法例子
public class GenericMethod1 {
    private static int add(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    private static <T> T genericAdd(T a, T b) {
        System.out.println(a + "+" + b + "="+a+b);
        return a;
    }

    public static void main(String[] args) {
        GenericMethod1.add(1, 2); //1+2=3
        GenericMethod1.<String>genericAdd("a", "b"); //a+b=ab
    }
}

3.泛型接口

定义一个泛型接口文件:public interface GenericIntercace<T>{},例如:

//定义一个泛型接口文件
public interface GenericIntercace<T> {
     T getData();
}

泛型接口分两种实现方法:

一是实现类不明确泛型接口的类型参数变量,这时实现类也必须定义类型参数变量;

二是明确泛型接口的类型参数变量。

实现泛型接口方式一,不明确泛型接口的类型参数变量:public class ImplGenericInterface1<T> implements GenericIntercace<T>,例如:

//泛型接口方式一,不指定具体类型实现方式
public class ImplGenericInterface1<T> implements GenericIntercace<T> {
    private T data;

    private void setData(T data) {
        this.data = data;
    }

    @Override
    public T getData() {
        return data;
    }

    public static void main(String[] args) {
        ImplGenericInterface1<String> implGenericInterface1 = new ImplGenericInterface1<>();
        implGenericInterface1.setData("Generic Interface1");
        System.out.println(implGenericInterface1.getData()); //Generic Interface1
    }
}

实现泛型接口方式二,明确泛型接口的类型参数变量:public class ImplGenericInterface2 implements GenericIntercace<String> {},例如:

//泛型接口方式二,指定具体类型实现方式
public class ImplGenericInterface2 implements GenericIntercace<String> {
    @Override
    public String getData() {
        return "Generic Interface2";
    }

    public static void main(String[] args) {
        ImplGenericInterface2 implGenericInterface2 = new ImplGenericInterface2();
        System.out.println(implGenericInterface2.getData()); //Generic Interface2
    }
}

4.泛型通配符

Java中的泛型通配符是指 "?" 符号,表示一个未知类型的通配符。它可以用作方法参数、方法返回值和类中的成员变量等地方。

extends关键字设定上行边界,即指明参数类型的顶层类,限定实例化泛型类时传入的具体类型,只能是继承自顶层类的。

super关键字设置下行边界,即指定参数类型的底层类,限定传入的参数类型只能是设定类的父类。

通配符有以下三种使用方式:

<? extends T> 表示类型上界,表示类型必须是T或T的子类。

<? super T> 表示类型下界,表示类型必须是T或T的父类。

<?> 表示无限制通配符,表示可以是任意类型。

使用通配符可以使代码更加灵活,特别是在涉及到多态性和继承关系时。但需要注意,对于带有通配符的泛型类型,不能对其中的元素进行添加操作,只能进行读取操作。

下面给出 Java 泛型通配符示例

★以下是一个使用<? extends T>泛型通配符的示例代码

//<? extends T>通配符示例
import java.util.ArrayList;
import java.util.List;

public class ExampleA {

    public static void main(String[] args) {
        List<? extends Number> list = new ArrayList<>();
        
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);

        List<Double> doubleList = new ArrayList<>();
        doubleList.add(1.0);
        doubleList.add(2.0);

        // 下面两行都会编译报错,
        // 因为在声明时使用了 <? extends Number> 上界并不能允许添加元素。
        // list.add(3); 
        // list.add(3.0);

        // 可以读取列表的元素,读取操作是允许的。
        int sum = 0;
        for (Number n : intList) {
            //System.out.println(n);
            sum += n.intValue();            
        }
        System.out.println("sum: " + sum);//sum: 3 
        double sumB = 0;
        for (Number n : doubleList) {
            //System.out.println(n);
            sumB += n.doubleValue();       
        }
        System.out.println("sum: " + sumB);//sum: 3.0
    }
}

在这个例子中,我们声明了一个 List<? extends Number> 类型的列表 list,它表示一个包含 Number 类型或其子类类型的列表。尽管 list 是一个列表类型的变量,但是我们不能向其添加任何元素,因为 <? extends Number> 上界限制了可以添加到列表中的元素类型。我们可以安全地从 list 中读取元素,并且这些元素都是 Number 类型或其子类类型。

★以下是一个使用<? super T>泛型通配符的示例代码:

//<? super T>通配符示例
import java.util.ArrayList;
import java.util.List;

public class ExampleB {
    public static void main(String[] args) {
        List<Number> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2.0);
        List<? super Integer> integers = numbers;
        integers.add(3);
        System.out.println(integers);  // 输出 [1, 2.0, 3]
    }
}

这个示例中,我们首先创建了一个List<Number>对象,并向其中添加了两个元素:整数1和浮点数2.0。然后,我们将这个列表赋值给一个通配符类型的变量integers,该变量声明为<? super Integer>,表示它可以接受所有Integer的超类型(包括Integer本身)。最后,我们向integers列表中添加了一个整数3,这是完全合法的,因为Integer是Number的子类型。输出结果为[1, 2.0, 3]。

★以下是一个使用<? >泛型通配符的示例

例1、代码:

//<?>通配符示例
import java.util.*;

public class ExampleC {
   public static void printList(List<?> list) {
      for (Object elem : list)
         System.out.print(elem + " ");
      System.out.println();
   }

   public static void main(String[] args) {
      List<Integer> intList = Arrays.asList(1, 2, 3);
      List<String> strList = Arrays.asList("one", "two", "three");

      printList(intList); //1 2 3 
      printList(strList);  //one two three 
   }
}

在此示例中,我们定义了一个名为“printList”的静态方法,它接受一个未知类型的List作为参数,并打印每个元素。然后,我们创建一个Integer类型的List和一个String类型的List,并将它们分别传递给printList方法。由于该方法的参数类型是通配符,它可以接受任何类型的List,因此这两个列表都可以成功打印其元素。

例2、再给出一个<?>泛型通配符示例代码:

//又一个<?>泛型通配符示例
public class Example<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public static void printValue(Example <?> example) {
        System.out.println(example.getValue());
    }

    public static void main(String[] args) {
        Example<Integer> intExample = new Example<Integer>();
        intExample.setValue(10);
        printValue(intExample); //10

        Example<String> stringExample = new Example<String>();
        stringExample.setValue("Hello, world!");  
        printValue(stringExample); //Hello, world!
    }
}

在上面的示例中,printValue 方法的参数类型为 Example <?>,这表示可以接受任何类型的 Example 实例,但是我们无法知道它的具体类型。这个通配符 ? 限制了我们对其进行操作的能力,但是仍然可以打印出值。

Java泛型类型可以继承或实现其他泛型类型或非泛型类型,但遵循以下规则:

泛型类型不能直接继承非泛型类型。

子类中的泛型类型可以比父类中的泛型类型具体化(即指定更具体的类型),但不能泛化(即使用更广泛的类型)。

当使用泛型类作为参数时,如果子类中的泛型类型比父类中的泛型类型具体化,则可以传递子类对象作为参数;反之则不行。

使用通配符(?)可以传递任意类型的泛型对象作为参数,但无法在方法中使用该泛型类型(因为我们无法确定其具体类型)。

总之,泛型类型之间的继承关系必须满足类型匹配的规则。

附录

Java 泛型详解https://www.cnblogs.com/coprince/p/8603492.html

Java 中的泛型 https://blog.csdn.net/weixin_45395059/article/details/126006369

官方介绍

Lesson: Generics https://docs.oracle.com/javase/tutorial/extra/generics/

Generics (Updated) https://docs.oracle.com/javase/tutorial/java/generics/index.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学习&实践爱好者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值