java泛型详解

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

1 泛型类

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

这种定义只能给object设置string类型的,我们开看泛型

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<Integer> integerBox = new Box<Integer>();
Box<Double> doubleBox = new Box<Double>();
Box<String> stringBox = new Box<String>();

2 泛型方法

public class Main {

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

    public static void main(String[] args) {
        out("findingsea");
        out(123);
        out(11.11);
        out(true);
    }
}

给出不同的变量类型,并输出他们的类民,下面是一个泛型类和泛型方法的结合

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);

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

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);
}

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;
}
----通配符

首先我们先定义几个简单的类,下面我们将用到它:

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

public class GenericReading {
    static List<Apple> apples = Arrays.asList(new Apple());
    static List<Fruit> fruit = Arrays.asList(new Fruit());
   static class CovariantReader<T> {
    //返回的并不是list,注意是获取具体某一个元素
    T readCovariant(List<? extends T> list) {
        return list.get(0);
    }
}
static void f2() {
    CovariantReader<Fruit> fruitReader = new CovariantReader<Fruit>();
    Fruit f = fruitReader.readCovariant(fruit);
    Fruit a = fruitReader.readCovariant(apples);
}
public static void main(String[] args) {
    f2();
}
}

有没有觉得很爽酸。。。
有以下几点问题需要注意:
(1):

在Java中不允许创建泛型数组,类似下面这样的做法编译器会报错:
List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error

以下代码可以得出

public class ErasedTypeEquivalence {
    public static void main(String[] args) {
        Class c1 = new ArrayList<String>().getClass();
        Class c2 = new ArrayList<Integer>().getClass();
        System.out.println(c1 == c2); // true
    }
}

(2):
Java泛型很大程度上只能提供静态类型检查,然后类型的信息就会被擦除,所以像下面这样利用类型参数创建实例的做法编译器不会通过:

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

//利用反射解决
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

List<String> ls = new ArrayList<>();
append(ls, String.class);

(3):
我们无法对泛型代码直接使用instanceof关键字,因为Java编译器在生成代码的时候会擦除所有相关泛型的类型信息

public static <E> void rtti(List<E> list) {
    if (list instanceof ArrayList<Integer>) {  // compile-time error
        // ...
 
//解决方法  
public static void rtti(List<?> list) {
    if (list instanceof ArrayList<?>) {  // OK; instanceof requires a reifiable type
        // ...
    }
}

工厂模式

接下来我们利用泛型来简单的实现一下工厂模式,首先我们先声明一个接口Factory:

public interface qqq<t>:{
}

下面创建几个类
class Filter extends Part {}
class FuelFilter extends Filter {
    public static class Factory implements typeinfo.factory.Factory<FuelFilter> {
        public FuelFilter create() {
            return new FuelFilter();
        }
    }
}
class AirFilter extends Filter {
    public static class Factory implements typeinfo.factory.Factory<AirFilter> {
        public AirFilter create() {
            return new AirFilter();
        }
    }
}

class Belt extends Part {}
class FanBelt extends Belt {
    public static class Factory implements typeinfo.factory.Factory<FanBelt> {
        public FanBelt create() {
            return new FanBelt();
        }
    }
}
class GeneratorBelt extends Belt {
    public static class Factory implements typeinfo.factory.Factory<GeneratorBelt> {
        public GeneratorBelt create() {
            return new GeneratorBelt();
        }
    }
}

有关内部类自己可以google查阅

class Part {
    static List<Factory<? extends Part>> partFactories =
        new ArrayList<Factory<? extends Part>>();
    static {
       // 注意内部类的调用方法
        partFactories.add(new FuelFilter.Factory());
        partFactories.add(new AirFilter.Factory());
        partFactories.add(new FanBelt.Factory());
        partFactories.add(new PowerSteeringBelt.Factory());
    }
    private static Random rand = new Random(47);
    public static Part createRandom() {
        int n = rand.nextInt(partFactories.size());
        return partFactories.get(n).create();
    }
    public String toString() {
        return getClass().getSimpleName();
    }
}

看完工厂的代码觉得很精妙,现在觉得java代码还是很好玩的,下回有机会ji xu

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 2.6 Java泛型详解 Java泛型是JDK5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type...
    jianhuih阅读 700评论 0 3
  • 一、泛型简介 1.引入泛型的目的 了解引入泛型的动机,就先从语法糖开始了解。 语法糖 语法糖(Syntactic ...
    Ruheng阅读 4,664评论 2 50
  • 一、引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用Arr...
    Q南南南Q阅读 549评论 0 1
  • 孙恩兵败自杀后,剩余的部众又推举孙恩的妹夫卢循为首领,继续在东南沿海骚乱。 卢循是东汉名儒卢植之后,这个卢植可是个...
    寒七琪阅读 415评论 0 1