欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识

既然这样上面的第 昆山软件开发 3行代码不应该报错

点击: 次  来源:宝鼎软件 时间:2017-08-27

原文出处: ZIWENXIE

泛型是Java中一个很是重要的常识点,在Java荟萃类框架中泛型被遍及应用。本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理惩罚,以及让人苦恼的范例擦除。

泛型基本

泛型类

我们首先界说一个简朴的Box类:

public class Box {
    private String object;
    public void set(String object) { this.object = object; }
    public String get() { return object; }
}

这是最常见的做法,这样做的一个弊端是Box内里此刻只能装入String范例的元素,此后假如我们需要装入Integer等其他范例的元素,还必需要别的重写一个Box,代码得不到复用,利用泛型可以很好的办理这个问题。

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

这样我们的Box类便可以获得复用,我们可以将T替换成任何我们想要的范例:

Box<Integer> integerBox = new Box<Integer>();
Box<Double> doubleBox = new Box<Double>();
Box<String> stringBox = new Box<String>();

泛型要领

看完了泛型类,接下来我们来相识一下泛型要领。声明一个泛型要领很简朴,只要在返回范例前面加上一个雷同<K, V>的形式就行了:

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}
public class Pair<K, V> {
    private K key;
    private V value;
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

我们可以像下面这样去挪用泛型要领:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

可能在Java1.7/1.8操作type inference,让Java自动推导出相应的范例参数:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

界线符

此刻我们要实现这样一个成果,查找一个泛型数组中大于某个特定元素的个数,我们可以这样实现:

public static <T> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // compiler error
            ++count;
    return count;
}

可是这样很明明是错误的,因为除了short, int, double, long, float, byte, char等原始范例,其他的类并不必然能利用操纵符>,所以编译器报错,那怎么办理这个问题呢?谜底是利用界线符。

public interface Comparable<T> {
    public int compareTo(T o);
}

做一个雷同于下面这样的声明,这样就便是汇报编译器范例参数T代表的都是实现了Comparable接口的类,这样便是汇报编译器它们都至少实现了compareTo要领。

public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

通配符

在相识通配符之前,我们首先必需要澄清一个观念,照旧借用我们上面界说的Box类,假设我们添加一个这样的要领:

public void boxTest(Box<Number> n) { /* ... */ }

那么此刻Box<Number> n答允接管什么范例的参数?我们是否可以或许传入Box<Integer>可能Box<Double>呢?谜底是否认的,固然Integer和Double是Number的子类,可是在泛型中Box<Integer>可能Box<Double>与Box<Number>之间并没有任何的干系。这一点很是重要,接下来我们通过一个完整的例子来加深一下领略。

首先我们先界说几个简朴的类,下面我们将用到它:

class Fruit {}
class Apple extends Fruit {}
class Orange extends Fruit {}

下面这个例子中,我们建设了一个泛型类Reader,然后在f1()中当我们实验Fruit f = fruitReader.readExact(apples);编译器会报错,因为List<Fruit>与List<Apple>之间并没有任何的干系。

public class GenericReading {
    static List<Apple> apples = Arrays.asList(new Apple());
    static List<Fruit> fruit = Arrays.asList(new Fruit());
    static class Reader<T> {
        T readExact(List<T> list) {
            return list.get(0);
        }
    }
    static void f1() {
        Reader<Fruit> fruitReader = new Reader<Fruit>();
        // Errors: List<Fruit> cannot be applied to List<Apple>.
        // Fruit f = fruitReader.readExact(apples);
    }
    public static void main(String[] args) {
        f1();
    }
}