美文网首页
Java泛型总结

Java泛型总结

作者: ObadiObada | 来源:发表于2017-12-01 15:24 被阅读0次

Java泛型总结

概述

泛型JDK1.5之引入,其含义是 “参数化类型”。即在定义时将具体的类型参数化,在使用时再将具体的类型传入,即数据的具体的类型是一个参数,在真正使用时传入实际的类型。例如:

void testNonGenericArrayList() {
    List list = new ArrayList();
    list.add("ABC");
    list.add(new Integer(100));

    for (Object o : list) {
        String s = (String) o;
        System.out.print("Object is " + s);
    }
}

上述的代码回出现 ClassCastException。因为Object无法转换成String类型。如果将List改为使用泛型定义

List<String> list = new ArrayList<String>();

那么在编译阶段

list.add(new Integer(100));

就会出现异常。

泛型使用方法

泛型可以作用在类,接口,方法中,使用了泛型的类,接口,方法就被称为泛型类,泛型接口,泛型方法。

泛型类

通过泛型类,可以抽象对一系列类型的通用操作,一个泛型类的基本写法为:

static class GenericClass<T> {
    T mField;

    GenericClass(T field) {
        mField = field;
    }

    String getDescription() {
        return "Class is " + mField.getClass() + " values is " + mField;
    }
}

其中<T>即为参数化类型,使用时中需要以真正的类型代替。使用如下:

GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
System.out.print("stringGeneric :" + stringGeneric.getDescription() + " \r\n");
System.out.print("integerGeneric :" + integerGeneric.getDescription() + " \r\n");

输出结果:

stringGeneric :Class is class java.lang.String values is ABC

integerGeneric :Class is class java.lang.Integer values is 65535

从代码看,这个泛型类抽象了“打印承运类名称和值的操作”。

泛型接口

泛型接口的定义方法和泛型类类似

interface ComputableItem<T> {
    T doubleSelf();
}
接口继承

继承接口有两种方法:

  • 不传人参数化类型
interface AdvancedComputableItem<T> extends ComputableItem<T> {
    int tripeSelf();
}

这种情况下子接口必须包含和父接口一致的参数化类型。

  • 传入参数化类型
interface ComputableString extends ComputableItem<String> {
    String tripeSelf();
}

这种情况下子接口无需包含参数化类型。

接口实现

接口的实现类有下面三种形式:

  • 传入实例化类型
class AdvancedComputableInteger implements AdvancedComputableItem<Integer> {

    Integer mField;

    public Integer doubleSelf() {
        return mField * 2;
    }

    public Integer tripeSelf() {
        return mField * 3;
    }

}
  • 继承已经传入了实例化类型的子类

class ConnectableString implements ComputableString {

    String mField;

    public String doubleSelf() {
        return mField + " : " + mField;
    }

    public int tripeSelf() {
        return mField.hashCode() % 5 * 3;
    }

}
  • 通过泛型类实现
class NullComputableItem<T> implements ComputableItem<T> {

    public T doubleSelf() {
        return null;
    }

}

泛型方法

泛型方法是泛型中使用较为复杂的,一个例子如下:

public <T> T createT(Class<T> tClass) {
    T t = null;
    try {
        t = tClass.newInstance();
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return t;
}

其中<T>表示结构化参数, 使用方法如下:

String str = createT(String.class);

泛型通配符

观察下述代码:

/**
 * @param args
 */
public static void main(String[] args) {
    GenericClass<Number> numberGeneric = new GenericClass<Number>(0);
    GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
    printGeneric(numberGeneric);
    printGeneric(integerGeneric);
}

static void printGeneric(GenericClass<Number> number) {
    System.out.print("printGeneric :" + number.getDescription() + " \r\n");
}

static class GenericClass<T> {

    T mField;

    GenericClass(T field) {
        mField = field;
    }

    String getDescription() {
        return "Class is " + mField.getClass() + " values is " + mField;
    }
}

虽然IntegerNumber的子类,但是编译器仍然会提示printGeneric(integerGeneric)错误。因为IntegerNumber都只是类型参数而已。JDK提供了一种叫做类型统配符的方法来处理这种情况:

static void printGeneric(GenericClass<?> number) {
    System.out.print("printGeneric :" + number.getDescription() + " \r\n");
}

这里的?表示通配符,即类型实参。

泛型边界

假设有三个类,其中People是父类,Male和Female是子类,定义泛型类时,可以限定类型参数的范围

static class People {

    protected String dress() {
        return "Dress cloth";
    }

    @Override
    public String toString() {
        return dress();
    }
}

static class Male extends People {

    @Override
    protected String dress() {
        return "Dress Suit";
    }
}

static class Female extends People {

    @Override
    protected String dress() {
        return "Dress Skirt";
    }
}

static class GenericClass<T extends People> {

    T mField;

    GenericClass(T field) {
        mField = field;
    }

    String getDescription() {
        return "Class is " + mField.getClass() + " values is " + mField;
    }
}

将类型参数限定后,编译器会对不符合限定的类型实参报错,例如下面这种写法:

GenericClass<Integer> integerGeneric = new GenericClass<Integer>(1);

同样泛型的编辑也可以作用于泛型方法:

static void printGeneric(GenericClass<?> item) {
    System.out.print("printGeneric :" + item.getDescription() + " \r\n");
}

泛型擦除

观察下面一组代码:

public static void main(String[] args) {
    // GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
    // GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
    // System.out.print("stringGeneric :" + stringGeneric.getDescription() + " \r\n");
    // System.out.print("integerGeneric :" + integerGeneric.getDescription() + " \r\n");
    // String str = createT(String.class);
    // GenericClass<Number> numberGeneric = new GenericClass<Number>(0);
    // GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
    // printGeneric(numberGeneric);
    // printGeneric(integerGeneric);
    // GenericTest gt = new GenericTest();
    // printGeneric(Male.class, gt);
    // printGeneric(Female.class, gt);

    GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
    GenericClass<Integer> integerGeneric = new GenericClass<Integer>(100);
    System.out.print("String :" + stringGeneric.getClass() + " \r\n");
    System.out.print("Integer:" + integerGeneric.getClass() + " \r\n");
    String str = stringGeneric.get();
    Integer integer = integerGeneric.get();
    System.out.print("str:" + str + " integer:" + integer);

}

static class GenericClass<T> {
    T mField;

    GenericClass(T field) {
        mField = field;
    }

    T get() {
        return mField;
    }

    String getDescription() {
        return "Class is " + mField.getClass() + " values is " + mField;
    }

}

输出结果为:

String class GenericTest$GenericClass

Integer class GenericTest$GenericClass

str:ABC integer:100

从输出结果看无论类型参数如何传输,泛型的类都是一样的,我们从字节码来观察下,main函数字节码如下:

 public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class GenericTest$GenericClass
       3: dup           
       4: ldc           #3                  // String ABC
       6: invokespecial #4                  // Method GenericTest$GenericClass."<init>":(Ljava/lang/Object;)V
       9: astore_1      
      10: new           #2                  // class GenericTest$GenericClass
      13: dup           
      14: bipush        100
      16: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      19: invokespecial #4                  // Method GenericTest$GenericClass."<init>":(Ljava/lang/Object;)V
      22: astore_2      
      23: aload_1       
      24: invokevirtual #6                  // Method GenericTest$GenericClass.get:()Ljava/lang/Object;
      27: checkcast     #7                  // class java/lang/String
      30: astore_3      
      31: aload_2       
      32: invokevirtual #6                  // Method GenericTest$GenericClass.get:()Ljava/lang/Object;
      35: checkcast     #8                  // class java/lang/Integer
      38: astore        4
      40: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      43: new           #10                 // class java/lang/StringBuilder
      46: dup           
      47: invokespecial #11                 // Method java/lang/StringBuilder."<init>":()V
      50: ldc           #12                 // String str:
      52: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      55: aload_3       
      56: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      59: ldc           #14                 // String  integer:
      61: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      64: aload         4
      66: invokevirtual #15                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      69: invokevirtual #16                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      72: invokevirtual #17                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
      75: return 

可以看到 24行调用get方法返回类型为Object,第27行将Object转换成String。同样32行和35行也有类似的操作。

泛型数组

Java中不允许声明泛型数组,即下述写法是非法的:

List<String>[] ls = new ArrayList<String>[10];  

但是可以通过通配符声明:

List<?>[] ls = new ArrayList<?>[10];  

具体的原因Sun官网的一篇文章以及本文的参考文档上都有有介绍

参考文档

java 泛型详解

相关文章

  • Java泛型

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

  • Java 泛型

    java 泛型 很多朋友对java的泛型不是很理解,很多文章写的已不是很清楚,这篇博客对java泛型进行 一个总结...

  • java泛型

    本质:类型参数化 java总结——泛型

  • Java泛型教程

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

  • java 泛型

    很多朋友对Java的泛型不是很理解,很多文章写的已不是很清楚,这篇博客对java泛型进行 一个总结。 泛型的转换:...

  • 第二十八课:泛型

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

  • Kotlin 泛型

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

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

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

  • Java泛型总结

    Java泛型总结# 泛型是什么## 从本质上讲,泛型就是参数化类型。泛型十分重要,使用该特性可以创建类、接口以及方...

  • 泛型程序设计

    (Java基础篇都是根据《Java核心技术 卷I》再进行自己的总结归纳和思考写出的) 泛型是什么? 泛型字面上就是...

网友评论

      本文标题:Java泛型总结

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