泛型是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 stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
Box integerBox = new Box();
Box doubleBox = new Box();
Box stringBox = new Box();
2 泛型方法
public class Main {
public static 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 boolean compare(Pair p1, Pair p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
public class Pair {
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 p1 = new Pair<>(1, "apple");
Pair p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);
现在我们要实现这样一个功能,查找一个泛型数组中大于某个特定元素的个数,我们可以这样实现:
public static 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 {
public int compareTo(T o);
}
public static > 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 apples = Arrays.asList(new Apple());
static List fruit = Arrays.asList(new Fruit());
static class CovariantReader {
//返回的并不是list,注意是获取具体某一个元素
T readCovariant(List extends T> list) {
return list.get(0);
}
}
static void f2() {
CovariantReader fruitReader = new CovariantReader();
Fruit f = fruitReader.readCovariant(fruit);
Fruit a = fruitReader.readCovariant(apples);
}
public static void main(String[] args) {
f2();
}
}
有没有觉得很爽酸。。。
有以下几点问题需要注意:
(1):
在Java中不允许创建泛型数组,类似下面这样的做法编译器会报错:
List[] arrayOfLists = new List[2]; // compile-time error
以下代码可以得出
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList().getClass();
Class c2 = new ArrayList().getClass();
System.out.println(c1 == c2); // true
}
}
(2):
Java泛型很大程度上只能提供静态类型检查,然后类型的信息就会被擦除,所以像下面这样利用类型参数创建实例的做法编译器不会通过:
public static void append(List list) {
E elem = new E(); // compile-time error
list.add(elem);
}
//利用反射解决
public static void append(List list, Class cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
List ls = new ArrayList<>();
append(ls, String.class);
(3):
我们无法对泛型代码直接使用instanceof关键字,因为Java编译器在生成代码的时候会擦除所有相关泛型的类型信息
public static void rtti(List list) {
if (list instanceof ArrayList) { // compile-time error
// ...
//解决方法
public static void rtti(List> list) {
if (list instanceof ArrayList>) { // OK; instanceof requires a reifiable type
// ...
}
}
工厂模式
接下来我们利用泛型来简单的实现一下工厂模式,首先我们先声明一个接口Factory:
public interface qqq:{
}
下面创建几个类
class Filter extends Part {}
class FuelFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory {
public FuelFilter create() {
return new FuelFilter();
}
}
}
class AirFilter extends Filter {
public static class Factory implements typeinfo.factory.Factory {
public AirFilter create() {
return new AirFilter();
}
}
}
class Belt extends Part {}
class FanBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory {
public FanBelt create() {
return new FanBelt();
}
}
}
class GeneratorBelt extends Belt {
public static class Factory implements typeinfo.factory.Factory {
public GeneratorBelt create() {
return new GeneratorBelt();
}
}
}
有关内部类自己可以google查阅
class Part {
static List> partFactories =
new ArrayList>();
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