xml地图|网站地图|网站标签 [设为首页] [加入收藏]

智能家电

当前位置:美高梅游戏网站 > 智能家电 > Java 泛型解读

Java 泛型解读

来源:http://www.gd-chuangmei.com 作者:美高梅游戏网站 时间:2019-09-21 21:06

对于泛型的使用我想大家都非常熟悉,但是对于类型擦除,边界拓展等细节问题,可能不是很清楚,所以本文会重点讲解一下;另外对泛型的了解其实可以看出,一个语言特性的产生逻辑,这对我们平时的开发也是非常有帮助的;

泛型的初认识

  1. 泛型是在JDK1.5之后增加的新功能。泛型(Generic)
  2. 泛型可以解决数据类型的安全性问题,在类的声明时通过一个标示表示类中某个属性的类型或者是某个方法的返回值及参数类型。
  3. 大致格式:
    • 访问权限 class 类名称 <泛型,泛型,...> {
      属性
      方法
      }
  4. 对象的创建: 类名称<具体类型> 对象名称 = new 类名称<具体类型>();
  • 将上篇泛型认识前篇中的 Point 类用泛型优化

class Point<T>{
private T x;
private T y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
public class Generics {
public static void main(String[] args) {
// TODO Auto-generated method stub
Point<Integer> point = new Point<Integer>();
point.setX(10);
point.setY(29);
System.out.println("打印坐标:"+
point.getX()+","+point.getY());
}
}

通过使用泛型的例子与上篇做相应代码比较可发现,在创建对象初始化坐标时比较方便,需要什么类型数据的坐标,直接在 <> 类加入相应的类名就行。

- 指定多个泛型,只需在尖括号 <> 中用逗号“,”分开就行。例如:

    ```
class Demo<T,K>{
    private T take;
    private K key;        
}

一、为什么会出现泛型

首先泛型并不是Java的语言特性,是直到 JDK1.5 才支持的特性(具体区别后面会讲到);那么在泛型出现之前是怎么做的呢?

List list = new ArrayList();list.add("123");String s =  list.get;

如上面代码所示,在集合里面需要我们自己记住放进去的是什么,取出来的时候再强转; 也就将这种类型转换的错误推迟到了运行时,即麻烦还不安全,所以才出现了泛型;

使用场景:泛型类,泛型接口,泛型方法;

public class Test<T>public interface Test<T>public <T> void test

泛型的构造方法

  • 构造方法可以为类中的属性初始化,那么如果类中的属性通过泛型指定,又需要构造方法来设置其内容的时候,那么构造方法的定语与普通类的构造方法并无大同,不需要像声明类那样指定泛型。
    • 在上诉代码 泛型类 Point 中加入构造方法:
public Point(T x,T y){
    this.x = x;
    this.y = y;
}

- 在对象实例化时使用构造方法:
```

Point<String> point1 = new Point<String>("经度1","纬度2");
```

二、泛型会带来什么样的问题

正如上面所讲泛型并不是 Java 一开始就具有的特性,所以在后来想要增加泛型的时候,就必须要兼容以前的版本,Sun 他们想到的折中解决方案就是类型擦除;意思就是泛型的信息只存在于编译期,在运行时期所有的泛型信息都被擦除了,就想没有一样;

List<String> list1 = new ArrayList<>();List<Integer> list2 = new ArrayList<>();System.out.println(list1.getClass;System.out.println(list2.getClass() == list1.getClass;

// 打印:
class java.util.ArrayList
true

可以看到 List<String>List<String> 在运行时其实都是一样的,都是class java.util.ArrayList;所以在使用泛型的时候需要牢记,在运行时期没有泛型信息,也无法获取任何有关参数类型的信息;所以凡是需要获取运行时类型的操作,泛型都不支持!

泛型 - 通配符

class Info<T>{
    private T key;
    public T getKey() {
        return key;
    }
    public void setKey(T key) {
        this.key = key;
    }
    @Override
    public String toString(){
        return this.getKey().toString();
    }
}

public class Generics2 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Info<String> info = new Info<String>();
        info.setKey("Java学习");
        read(info);
    }
    public static void read(Info<?> t) {
        //这里用 ?通配符代表任意类型,使用Object会出错,不写<?>会有警告
        System.out.println(t.toString());
    }
}

1. 不能用基本类型实例化类型参数

new ArrayList<int>();      // errornew ArrayList<Integer>();  // correct

因为类型擦除,会擦除到他的上界也就是 Object;而 Java 的8个基本类型的直接父类是 Number,所以基本类型不不能用基本类型实例化类型参数,而必须使用基本类型的包装类;

泛型接口

  • 泛型接口的创建于泛型类的创界并无大同,只是将 class 变为 interface, 但是实现接口的类必须将接口的方法实现。

2. 不能用于运行时类型检查

t instanceof T             // errort instanceof List<T>       // errort instanceof List<String>  // errort instanceof List          // correct

但是可以使用 clazz.isInstance(); 进行补偿;

泛型方法

  • 泛型方法与泛型类并没有必然的联系,泛型方法有自己的类型参数,在普通类中也可以定义泛型方法。泛型方法中的T1、T2与泛型类中的T1、T2没有必然的联系,也可以使用其他的标识符来代替。
  • 泛型方法中可以定义泛型参数,此时参数的类型就是传入的数据类型
class Gener{
    //泛型方法 返回一 T 类型的数据 
    public <T> T returnT(T t) { 
        return t;    
        }
}
public class Generics3 {

    public static void main(String[] args) {
        Gener gener = new Gener();
        //泛型方法的调用方便传入任意类型的数据
        String str  = gener.returnT("Java学习");
        System.out.println(str);
        int i = gener.returnT(10);
        System.out.println(i);
    }
}

3. 不能创建类型实例

T t = new T();  // error

同样可以使用 clazz.newInstance(); 进行补偿;

泛型数组

  • 在创建一个泛型方法时,也可以传递或者返回一个泛型数组
class Gener{
    //传递一个泛型数组
    public <T> void returnT(T t[]) { 
            for (int i = 0; i < t.length; i++) {
                System.out.print(t[i]);
            }
        }
}
public class Generics3 {

    public static void main(String[] args) {
        Gener gener = new Gener();
        String arr[] ={"Java","学","习"};
        gener.returnT(arr);
    }
}

4. 不能静态化

private static T t;                  // errorprivate T t;                         // correctprivate static List<T> list;         // errorprivate static List<?> list;         // correctprivate static List<String> list;    // correct// e.g.class Test<T> {  private T t;  public void set { t = arg; }  public T get() { return t; }}

因为静态变量在类中共享,而泛型类型是不确定的,所以泛型不能静态化;但是非静态的时候,编译期可以根据上下文推断出T是什么,例如:

Test l = new Test();System.out.println;l.set("123");System.out.println;// javap -v 反编译12: invokevirtual #15         // Method JDK/Test14_genericity$Test.get:()Ljava/lang/Object;15: invokevirtual #16         // Method java/io/PrintStream.println:(Ljava/lang/Object;)V18: aload_119: ldc       #17             // String 12321: invokevirtual #18         // Method JDK/Test14_genericity$Test.set:(Ljava/lang/Object;)V24: getstatic   #6            // Field java/lang/System.out:Ljava/io/PrintStream;// ---------------------------Test l = new Test();System.out.println;l.set("123");System.out.println;// javap -v 反编译12: invokevirtual #15         // Method JDK/Test14_genericity$Test.get:()Ljava/lang/Object;15: invokevirtual #16         // Method java/io/PrintStream.println:(Ljava/lang/Object;)V18: aload_119: bipush    12321: invokestatic  #17         // Method java/lang/Integer.valueOf:Ljava/lang/Integer;

根据上面的代码,可以很清楚的看到,编译器对非静态类型的推导;

另外 List<?>List<String> 之所以是正确的,仍然是因为编译器可以在编译期间就能确定类型转换的正确性;

泛型摩擦

  • 如在定义某一个泛型类,在实例化过程中,没有指明具体类型,那么就是自动擦除泛型类型。编译器就将生成一种与泛型类同名的原始类,但是类型参数都被删除了。

5. 不能抛出或捕获泛型类的实例

catch                         // errorclass Test<T> extends Throwable    // error

因为在捕捉异常时候需要运行时类信息,并且判断异常的继承关系,所以不能抛出或捕获泛型类的实例;

总结 - 泛型的好处

  1. 类型安全
  2. 消除强制类型转换
  3. 潜在性能收益

6. 不允许作为参数进行重载

void test(List<Integer> list)void test(List<String> list)

因为在运行时期泛型信息被擦除,重载的两个方法签名就完全一样了;

本文由美高梅游戏网站发布于智能家电,转载请注明出处:Java 泛型解读

关键词:

上一篇:没有了

下一篇:没有了