美文网首页
java泛型

java泛型

作者: echoSuny | 来源:发表于2020-04-23 18:16 被阅读0次

什么是泛型?

Java泛型(generics)是JDK5中引入的一个新特性。泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数类型,也就是说所操作的数据类型被指定为一个参数。泛型不存在于JVM虚拟机中。

泛型的基本常识

类型参数和类型变量

Generic<T> 中的T被称为类型参数
Generic<String> 中的String被称为类型变量

常用的泛型

常用的泛型有 T, K, V, E 等,分别对应Type,Key,Value,Element。关于使用哪个字母没有明确的规定,只不过这是一种约定俗成的用法。定义泛型的时候尽量能让使用者知道大概的意思。

原始类型

缺少实际类型变量的泛型就是一个原始类型。例如:

class Generic<T> { }

如果在使用的过程中 Generic generic = new Generic() 不用泛型类型来使用这个类,那么这个Generic 就是 Generic<T> 的原始类型。

使用泛型的好处

1 安全性

本来虚拟机会在运行的时候进行类型检测,如果类型不对,则会抛出 ClassCastException 。引入了泛型机制之后,就可以在编译期间发现错误,相当于把发现错误的时间提前了。

        List list = new ArrayList();
        list.add("张无忌");
        list.add(1);

        for (int i = 0; i < list.size(); i++) {
            String str = (String) list.get(i);
            System.out.println(str);
        }
ClassCastException

上述代码在编译期是没有问题的,但当真正运行加载到内存的时候就会报类转换异常。
如果我们加上泛型之后,在代码编写的时候编译器就会提示我们类型错误:


类型错误

从而就可以避免运行期抛出异常,是我们的程序更加安全。

2 复用性

假如我们现在有一个需求是需要打印一个泛型类型为String的集合的所有元素,那么我们可以这样定义:

public void printStringList(List<String> list) {
        if (list == null || list.isEmpty()) return;
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

如果以后需求变了,需要打印的泛型类型为 Integer,甚至是我们自己定义的类,那我们岂不是要写很多逻辑类似的代码。所以为了避免我们做这些无用功,我们可以借助泛型来实现一个模版方法来增加我们代码的复用性:

public <T> void printListGeneric(List<T> list) {
        if (list == null || list.isEmpty()) return;
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

Java中的泛型

1 泛型类
 public class Box<T> {
    private T data;

    public T getData() {
        return data;
    }

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

    public void print() {
        System.out.println("这个箱子装的是:" + data.toString());
    }
}

下面是泛型类的一个简单使用:

 public class Fruit {
        String name;

        public Fruit(String name) {
            this.name = name;
        }

        @NonNull
        @Override
        public String toString() {
            return name;
        }
    }

  public class Apple extends Fruit{
        public Apple(String name) {
            super(name);
        }
  }

  public class Orange extends Fruit{

        public Orange(String name) {
            super(name);
        }
  }
  Box<Apple> appleBox = new Box<>();
  appleBox.setData(new Apple("苹果"));
  appleBox.print();
        
  Box<Orange> orangeBox = new Box<>();
  orangeBox.setData(new Orange("橙子"));
  orangeBox.print();
泛型类的简单使用
2 泛型接口
interface Generator <T>{
        T next();
    }

泛型接口的实现有两种方式:

// 泛型接口指明具体类型
public class Mask implements Generator<String> {

        @Override
        public String next() {
            return null;
        }
    }
// 泛型类实现泛型接口的类型参数
public class Cap<T> implements Generator<T> {

        @Override
        public T next() {
            return null;
        }
    }
3 泛型方法
public <T> void genericMethod(T t) {
        
}

只有在修饰符和返回值之间有 <> 的才是泛型方法。不管是上面泛型类还是泛型接口中的任何带有泛型类型参数的方法(简而言之就是带参数中带有泛型 T )都不是泛型方法。泛型方法只有这一种定义方式。

public class GenericMethod<T> {

    public static class Fruit { }

    public static class Apple extends Fruit { }

    public static class Person { }

    public void show(T t) { }

    public <T> void show2(T t) { }
}

show()方法就是一个普通的方法,它只不过使用了泛型类中的类型参数,所以它要受到在创建对象时泛型的约束。而show2()是一个标准的泛型方法。它的 T 和 泛型类当中的 T 没有任何关系。因此不受约束。

限定类型变量

public static <T> T max (T a,T b){
        if (a.compareTo(b)>0)return a ; else return b;
    }

上面这段代码在编译器上是不通过的。这是因为 T 是一种未知的类型。而想要使用compareTo()方法则必须实现了 Comparable 接口。反过来说,只有实现了 Comparable 接口的类才能使用compareTo() 来进行比较。

public static <T extends Comparable> T max (T a,T b){
        if (a.compareTo(b)>0)return a ; else return b;
    }

extends表示继承,派生。用在泛型中后面可以跟接口,也可以使用类。表示泛型 T 必须是它们的子类或实现类。上面代码中的T extends Comparable 就限定了传入的类型变量则必须实现 Comparable 接口。同时还可以进行多重限定:

public static <T extends Comparable & Serializable> T max (T a,T b){
        if (a.compareTo(b)>0)return a ; else return b;
    }

上面的代码则表示传入的 T 要同时实现Comparable 和 Serializable接口才能被使用。现在假设再限定 T 必须是 A 类的子类该怎么办呢?根据上面的例子使用 & 符号连接起来就可以了。其实不然。如果extends 后面的限定参数是类类型的话,那么类必须放在第一个的位置:

public static <T extends A & Comparable & Serializable> T max (T a,T b){
        if (a.compareTo(b)>0)return a ; else return b;
    }

其实这是符合我们Java代码的习惯的。我们写的类也只能是先继承某个类,然后再实现其他的接口。另外类类型只能使用一个,这是因为Java是单继承的。

泛型的约束和局限性

泛型普通使用的局限 泛型和异常

通配符

extends 为泛型添加上边界,即传入的类型实参必须是指定类型及子类型

首先定义四个普通的类:

    public class Animal{ }

    public class Cat extends Animal{ }

    public class Dog extends Animal{ }

    public class SpottedDog extends Dog{ }

两个测试方法:

    // Box 是上面介绍泛型类时定义的
    public void print(Box<Animal> box){ }
    // 使用了通配符 ?  通配符通常用在方法上
    public void print2(Box<? extends Animal> box){ }

根据使用情况来看,虽然 Dog 和 Cat 在继承关系上都属于 Animal 的子类,但是Box<Dog> 和 Box< Cat> 与Box<Animal> 是没有任何继承关系的,只不过它们都是同一个泛型类罢了。


使用通配符

可以看到,这四个对象都可以使用。这里使用通配符 ?extends 限制了传入的泛型参数只能是Animal以及Animal的子类。假如说Animal有一个父类A,那么Box<A>是不能传入print2()的。
那么下面再看一个例子:



可以看到在setData()的时候是不可以的,而getData()的时候却只能获得父类 Animal,子类是不行的。因为在setData()的时候,编译器只知道传入的数据是Animal的子类,但是到底是哪一个子类编译器是不清楚的,所以setData()不允许使用。而getData()的时候不管之前里面有什么样的数据,但是它们的父类都是Animal,都是可以向上转型的,这是安全的,所以被允许。
总结一下就是extends设定了上边界,可被用于安全的访问数据。
super 为泛型添加下边界,即传入的类型实参必须是指定类型的类型及父类型

添加一个新的测试的方法:

    public void print3(Box<? super Dog> box){ }

根据使用结果来看使用了 super 之后,只能传入Dog以及它的父类Animal。也就是说 super 限定了传入参数的下边界。



可以看到在setData()的时候Dog以及他的子类SpottedDog可以被使用,平级的Cat不行是很正常的,因为它不是Dog的超类,那Animal为什么也不行呢?我们知道Object是所有类的父类,编译器无法知道确切的是哪一个父类。从下面的getData()便可以证明这一点。只有Dog以及它的子类能安全的转型为Dog,故可以被set进去。
总结一下就是 super限定了传入参数的下界,用于安全的设置数据。

相关文章

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • 第二十八课:泛型

    泛型出现之前 泛型出现之后 Java深度历险(五)——Java泛型

  • Kotlin 泛型

    说起 kotlin 的泛型,就离不开 java 的泛型,首先来看下 java 的泛型,当然比较熟悉 java 泛型...

  • java泛型中类型擦除的一些思考

    java泛型 java泛型介绍 java泛型的参数只可以代表类,不能代表个别对象。由于java泛型的类型参数之实际...

  • Java泛型

    参考:Java知识点总结(Java泛型) 自定义泛型类 自定义泛型接口 非泛型类中定义泛型方法 继承泛型类 通配符...

  • Java泛型—Java语法糖,只在编译有作用,编译后擦出泛型

    Java泛型—Java语法糖,只在编译有作用,编译后擦出泛型 在代码进入和离开的边界处,会处理泛型 Java泛型作...

  • JAVA 核心笔记 || [xxx] 泛型

    泛型 JAVA 的参数化类型 称为 泛型 泛型类的设计 Learn12.java 运行

  • 简单回顾Java泛型之-入门介绍

    什么时候开始有了Java泛型?什么是Java泛型?为什么要引入Java泛型?什么时候用到了泛型?可不可以给泛型下一...

  • Kotlin 泛型

    Kotlin 支持泛型, 语法和 Java 类似。例如,泛型类: 泛型函数: 类型变异 Java 的泛型中,最难理...

  • JAVA-泛型

    JAVA-泛型 sschrodinger 2018/11/15 简介 泛型是Java SE 1.5的新特性,泛型的...

网友评论

      本文标题:java泛型

      本文链接:https://www.haomeiwen.com/subject/ykcushtx.html