美文网首页
Java 泛型

Java 泛型

作者: 靖阿 | 来源:发表于2017-12-27 10:22 被阅读0次

泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.

泛型类

我们定义一个简单的泛型类, T称为泛型参数, G被称为泛型化了

class G<T> {

}

接着我们在内部定义一个泛型变量

class G<T> {
    T t;
}

然后我们再添加一个泛型方法泛型方法

class G<T> {
    T t;

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

下来我们来使用一下这个泛型类

G<String> g = new G<>();
g.setValue("value");

泛型方法

在一个非泛化的类中我们也可以直接定义泛化的方法

类型擦除

说到java中的泛型就不得不提泛型参数的类型擦除. 当java源码文件被编译成class文件的时候,编译器会将泛型中的类型参数擦除掉(其实class文件中还是会保留部分的泛型信息, 具体参考java虚拟机规范).

类定义的泛型参数T会被替换成具体类型, 一般为Object. 而<T>信息则会被擦除掉, 例如G就会替换成

class G {
    Object t;

    public void setValue(Object t) {
        this.t = t;
    }
}

而在引用该类型的时候则会擦除成

G g = new G();
g.setValue("value");

因此,java里的泛型类型安全是由编译器保证的, 在运行期是无法保证类型的安全的.

泛型类的继承关系

如果类被泛型化之后, 会对类本身的继承关系造成影响

public class TestGeneric {

    public static void main(String[] args) {
        SuperParam superParam = new SuperParam();

        G<SuperParam> g = new G<>();
        g.setValue(superParam);

        SubG1 subG1 = new SubG1();
        subG1.setValue(1);
        subG1.printValue("SubG1 printValue");
        subG1.print();

        SubG2 subG2 = new SubG2();
        subG2.setValue("SubG2 setValue");
        subG2.printValue("SubG2 printValue");
        subG2.print();

        SubG3<Integer> subG3 = new SubG3<>();
        subG3.setValue("SubG3 setValue");
        subG3.printValue("SubG3 printValue");
        subG3.print(3);

        SubG4<Integer> subG4 = new SubG4();
        subG4.setValue("SubG4 setValue");
        subG4.printValue("SubG4 printValue");
        subG4.print(4);
    }
}

class G<T> {
    T t;

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

    public void printValue(T t) {
        System.out.println(t);
    }

    public T getT() {
        return t;
    }
}

// SubG1继承了G的泛型参数. 但是SubG1本身是没有泛化的
class SubG1 extends G {
    public void print() {
        System.out.println(t);
    }
}

// 强制指定继承过来的T的类型为String
class SubG2 extends G<String> {
    public void print() {
        System.out.println("Super:" + t);
    }
}

// G的类型由继承过来的方法指定, SubG3则再次由本类自己指定类型
class SubG3<T> extends G {
    public void print(T t1) {
        System.out.println("Super:" + t + ". this:" + t1);
    }
}

// 指定G的类型为String, SubG4则仍然由本类进行泛型化
class SubG4<T> extends G<String> {
    public void print(T t1) {
        System.out.println("Super:" + t + ". this:" + t1);
    }
}

class SuperParam {
    public String toString() {
        return "SuperParam";
    }
}

结果为

SubG1 printValue
1
SubG2 printValue
Super:SubG2 setValue
SubG3 printValue
Super:SubG3 setValue. this:3
SubG4 printValue
Super:SubG4 setValue. this:4

需要特别指出的是, 在SubG1对象分别调用setValue()printValue()方法时分别使用了IntegerString俩个类型, 但是却没有产生任何异常信息.

泛型参数的继承关系

public class TestGeneric {

    public static void main(String[] args) {
        SuperParam superParam = new SuperParam();
        Param param = new Param();

        G<SuperParam> gSuperParam = new G<>();
        gSuperParam.setValue(superParam);
        gSuperParam.setValue(param);
        SuperParam t = gSuperParam.getT();

        G<Param> gParam = new G<>();
        gParam.setValue(param);
        gParam.setValue(superParam);    // compile error

        SubParam subParam = new SubParam();
        gParam.setValue(subParam);

    }
}

class G<T> {
    T t;

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

    public T getT() {
        return t;
    }
}

class SuperParam {}

class Param extends SuperParam {}

class SubParam extends Param {}

从这一行gParam.setValue(subParam);我们可以看到类型参数的继承结构和普通类型的继承结构的规则是一样的.

泛化在方法中的应用

public class TestGeneric {

    public static void main(String[] args) {

        print(new G<>());
        print(new G<SuperParam>());     // compile Error
        print(new SubG1());
        print(new SubG2());
        print(new SubG3());
        print(new SubG3<SuperParam>());
        print(new SubG4());
        print(new SubG4<SuperParam>());

        printSuperParam(new G<>());
        printSuperParam(new G<SuperParam>());
        printSuperParam(new G<Param>());        // compile Error
    }

    public static void print(G<String> gs) {}

    public static void printSuperParam(G<SuperParam> gs) {}
}

从上面的例子中我们可以看出, 泛化的类的泛型参数并没有对其类型判断造成影响, 子类化的参数仍然是编译通过的. 但是类型参数的继承再传递到方法时, 却被认为不是相同的类型.

通配符

?在泛型参数中作为通配符存在, 它一般和extendssuper关键字一起使用. 它表示不确定的一组类型, 例如和extends关键字一起使用就是表示继承自某个类的所有类型

extends

extends关键字是用来定义泛型参数的继承关系. 它表示我们的泛型参数继承自某个类型, 也被我们称为上界符.

我们修改一下G的类型定义,我们引入extends关键字

public class TestGeneric {

    public static void main(String[] args) {

        SuperParam superParam = new SuperParam();

        G<SuperParam> g = new G<>();
        g.setValue(superParam);

        SubG1 subG1 = new SubG1();
        subG1.setValue(1);
        subG1.printValue("SubG1 printValue");
        subG1.print();

    }
}

class G<T extends SuperParam> {
    T t;

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

    public void printValue(T t) {
        System.out.println(t);
    }

    public T getT() {
        return t;
    }
}

当使用extends关键字之后, 我们就将这个类的泛化信息固定了下来, 在实例化的时候, 其类型参数必须是继承自某类的子类型

如果我们在实例化的时候指定extends会发生什么呢?

public class TestGeneric {

    public static void main(String[] args) {
        SuperParam superParam = new SuperParam();

        G<? extends SuperParam> g = new G<>();
        g.setValue(superParam);     // compile error
        g.setValue(new Param());        // compile error
    }
}

class G<T> {
    T t;

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

class SuperParam {}

class Param extends SuperParam {}
class Param1 extends SuperParam {}

不推荐这种用法, 因为这种情况下如果我们可以对其使用Param或者Param1的类型, 那么这就和不使用泛型是一样的, 会引起类型转化异常.

但是我们却可以在另外一种情况下使用这个关键字

public class TestGeneric {

    public static void main(String[] args) {
        SuperParam superParam = new SuperParam();

        print(new G<>());   // 默认的是SuperParam类型
        print(new G<Param>());
    }

    public static void print(G<? extends SuperParam> g) {
        SuperParam t = g.getT();
    }
}

class G<T> {
    T t;

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

    public T getT() {
        return t;
    }
}

class SuperParam {}

class Param extends SuperParam {}

super

super作为一种下界符存在. 也就在具体使用时的参数都必须是泛型参数的父类才行.

G<? super SuperParam> g = new G<>();
g.setValue(new SuperParam());
g.setValue(new Param());

同样我们可以在方法中如此使用

public class TestGeneric {

    public static void main(String[] args) {
        print(new G<SuperParam>());
        print(new G<>());   // 默认是Param
    }

    public static void print(G<? super Param> g) {
        Object t = g.getT();
    }
}

class G<T> {
    T t;

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

    public T getT() {
        return t;
    }
}

class SuperParam {}

class Param extends SuperParam {}

相关文章

  • 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/cswygxtx.html