美文网首页
Java泛型食用笔记(一) -- 基本介绍

Java泛型食用笔记(一) -- 基本介绍

作者: 不智鱼 | 来源:发表于2017-07-04 23:01 被阅读110次

Java泛型食用笔记(一) -- 基本介绍


JDK5 将泛型引入,这是 Java 走向类型安全的一大步,然而,在学习和使用泛型的过程中,几乎都会遇到令人沮丧的问题。本系列试图将 Java 的泛型解释清楚,帮助开发者在开发中正确的使用泛型。

1. 没有泛型的糟糕世界

引入泛型的原因不少,其中一个重要原因就是为了容器类的优雅实现。

在没有泛型前,当需要实现一个可以存储任何类型对象的 Hashtable 时,会定义如下接口:

public class Hashtable {
...
    public Object put(Object key, Object value) {...}
    public Object get(Object key) {...}
...
}

当你向 Hashtable 中插入对象时,比如一个 String 对象,Hashtable 会把类型信息抹去,退化成 Object 对象进行存储。此时只有你的脑袋中存有类型信息,当你需要从 Hashtable 获取元素时,必须对其进行强制类型转换才能获得你所需的 String 对象。

...
    Hashtable h = new Hashtable();
    h.put("string", "value");
    String s = (String)h.get("string");
...

这样的代码直觉上就不太让人放心。每次强制类型转换,不仅增加冗余的代码,同时也是一次忽略编译器进行静态类型检查的过程,如果转换过程中出现错误,就会抛出 ClassCastException 异常,除非你能确保转换无误,否则你需要更过的代码来进行错误处理。在泛型出来之前,这几乎是无解题。

2. 泛型的引入

Java 泛型的核心就是告诉编译器想使用什么类型,然后编译器帮你处理一切细节。泛型也是一种参数,只不过参数传入的是类型。引入泛型可以让一些实现更优雅。

使用泛型重新定义 Hashtable 及其接口:

public class Hashtable<K, V> {
...
    public V put(K key, V value) {...}
    public V get(K key) {...}
...
}

其中 <K, V> 即为类型参数,其作用域为类定义的主体部分(除静态成员)。当使用泛型类时,你要将你所需使用的类型传入。

...
    Hashtable<String, String> h = new Hashtable<>();
    h.put("string", "value");
    String s = h.get("string");
...

使用泛型后的 Hashtable 类更加简洁。我们来看下泛型带来了什么:

  • 将类型信息告诉编译器
  • 放入元素时编译器进行类型检查
  • 取出元素自动转换类型

编译器在编译期间就会进行类型检查,防止在运行期间出现类型错误。

3. 泛型接口

泛型也可用于接口,比如需要写一个生成器:

interface Gernerator<T> {
    T next();
}

class DrinkGernerator implements Gernerator<Drink> {
    private Drink[] drinks = {new Water(){}, new Coke(){}, new Coffee(){}};
    private Random seed = new Random();

    @Override
    public Drink next() {
        return drinks[seed.nextInt(3)];
    }
}

abstract class Drink {
    public abstract String name();
}

class Water extends Drink {
    @Override
    public String name() {
        return "Water";
    }
}

class Coke extends Drink {
    @Override
    public String name() {
        return "Coke";
    }
}

class Coffee extends Drink {
    @Override
    public String name() {
        return "Coffee";
    }
}

看上去是不是特别眼熟,容器的迭代器 Iterator<E> 就是一个典型的生成器接口。

4. 泛型方法

之前据的所有例子都是作用与整个类的,泛型也可以仅仅应用在方法上,也就是接下来要介绍的泛型方法。原则上,能够使用泛型方法的时候就尽量避免使用泛型类,这会使你的代码看上去更加清楚。另外,如果 static 方法需要使用泛型,只能使用泛型方法。

泛型方法的使用方法就是将泛型参数置于返回值之前:

public class GernericMethod {
    public static <T> void printClassName(T t) {
        System.out.println(t.getClass().getName());
    }

    public static void main(String[] args) {
        printClassName("string");
        printClassName(1);
        printClassName(2.1);
    }
}

output:

java.lang.String
java.lang.Integer
java.lang.Double

看上去 printClassName 方法就像无限重载过,无论传入什么类型的参数,都可以顺利执行。这是因为泛型方法在使用时,编译器会进行参数推断,帮助我们找到具体的类型。

小结

泛型可以用于泛型类,泛型接口,泛型方法,并都有各自的使用场景。引入泛型后,可以避免很多蹩脚的类型转换等操作,让代码实现更为优雅,看上去一切都很美好。接下来我们将探讨 Java 泛型的实现原理及带来的问题,以帮助我们更好的理解和使用 Java 泛型

相关文章

  • Java泛型食用笔记(一) -- 基本介绍

    Java泛型食用笔记(一) -- 基本介绍 JDK5 将泛型引入,这是 Java 走向类型安全的一大步,然而,在学...

  • Java泛型食用笔记(三) -- 擦除的代价

    Java泛型食用笔记(三) -- 擦除的代价 为了兼容性,Java 采用了擦除来实现泛型,这也付出了一些代价。本节...

  • Java泛型基础(一)

    本文首发于个人网站:Java中的泛型(一) 本文主要介绍Java泛型的基本知识,包括目的、泛型类的基本用法和场景应...

  • Java基础之泛型

    Java基础之泛型 泛型基本介绍 简单介绍在jdk1.6之前,还没有使用到泛型,Java类型分为原始类型、复杂类型...

  • Java泛型食用笔记(二) -- 类型擦除

    Java泛型食用笔记(二) -- 类型擦除 在使用别人已经创建的泛型类时,你可能会感觉到泛型给你带来的诸多方便。但...

  • Java泛型食用笔记(四) -- 通配符

    Java泛型食用笔记(四) -- 通配符 1. 三种通配符 通配符为一个泛型类所指定的类型集合提供了一个有用的类型...

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

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

  • 泛型中 ? super T和 ? extends T的区别

    首先, 说到 Java 的泛型, 我们必须要提到的是Java 泛型的类型擦除机制: Java中的泛型基本上都是在编...

  • 解析java泛型(二)

    上篇我们简单的介绍了java中泛型的最基本的内容,知道了什么是泛型以及泛型对我们的程序编写有什么好处,最后以类型限...

  • Java泛型教程

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

网友评论

      本文标题:Java泛型食用笔记(一) -- 基本介绍

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