一般的类和方法,只能使用具体的类型,如基本类型(int,double
等),或者自定义的类。如果想要编写更为通用的代码,这种形式会带来一定的束缚。
Java 中的多态实际上已经可以实现一些场景下的通用代码编写,但是多态必须基于继承体系,这使得它也会受一定束缚。一个方法参数为某个接口,那么我们可以将这个接口实现的所有类的对象作为参数,而其他没有实现这个接口的类则无法作为参数。
Java SE5 引入了泛型概念,通过解耦类或方法与所使用的类型之间的约束实现,简单的说就是将类型参数化了。使代码可以应用于多种类型。
泛型可以应用于类(包括内部类,匿名类等)、接口和方法。
对于容器类而言,泛型很好的解决了容器类的类型问题,因为容器类需要能够持有所有对象类型,虽然 Object 可以表示任何对象,但是从容器中取出对象时需要进行类型转换,而泛型可以事先声明容器类的存储对象类型,因此不需要再考虑类型问题。
class SimpleList<T> {
private T[] arr;
private int count;
public SimpleList(T[] arr) {
this.arr = arr;
this.count = arr.length;
}
public void print() {
for(T t : arr) {
System.out.println(t.toString());
}
}
public T get(int i) {
if(i < count) {
return arr[i];
}
return null;
}
public void add(T t) {
this.arr[count++] = t;
}
}
public class Test {
public static void main(String[] args) {
Integer[] arr1 = new Integer[]{1,2,3,4,5,6,7};
SimpleList<Integer> list = new SimpleList<Integer>(arr1);
list.add(10);
list.print();
String[] arr2 = new String[]{"abc", "sij", "soie"};
SimpleList<String> list1 = new SimpleList<String>(arr2);
list.add("ssade");
list1.print();
}
}
上例中实现了一个简单的集合类,可以接受任何类型的数组对象。并且提供add()
方法添加元素。
在实际开发过程中,会不可避免的遇到想要一个返回多个值的方法,但是作为方法返回值却只能有一个,具体的类虽然可以创建包含多个数据的对象,但是不够通用,而泛型类可以解决这个问题,我们可以创建一个具有多个类型参数的泛型类作为返回对象的类型,这被叫做元组。
例如,我们要创建一个可以返回两个参数的二维元组,和一个继承自该二维元组的三维元组,理论上元组的维数是没有限制的。任何方法只要想要返回两个或者三个结果值的,都可以使用下面这两个元组。
class Two<T, K> {
public final T first;
public final K scond;
public Two(T t, K k) {
first = t;
scond = k;
}
@Override
public String toString() {
return "Two [first=" + first + ", scond=" + scond + "]";
}
}
class Three<T, K, L> extends Two<T, K> {
public final L three;
public Three(T t, K k, L l) {
super(t, k);
three = l;
}
@Override
public String toString() {
return "Three [three=" + three + "]";
}
}
可以观察到在这两个元组中,成员变量都是public final
公共的不可变的,由于final
保证了值的不可变,因此我们不需要像 javabean
那样将成员变量私有化。
泛型除了在类上的应用外,同样可以应用于接口。生成器,是工厂方法设计模式的一种应用,工厂方法设计模式通常需要批量生产不同类型的对象。
现在尝试来编写一个生成器接口以及实现一个具体的生成器,为了验证生成器的作用,额外编写需要被生成器使用的类。并且这个生成器还需要能够使用foreach
语法。
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;
/**
* 生成器接口
* @author Alan
*
* @param
*/
interface Generator<T> {
T next();
}
class PersonGenerator implements Generator<Person>, Iterable<Person>{
private Class<?>[] types = {Student.class, Teacher.class};
private static Random rand = new Random(100);
//Foreach语法中结束标志
private int size;
public PersonGenerator() {
}
public PersonGenerator(int size) {
this.size = size;
}
@Override
public Iterator<Person> iterator() {
return new Iterator<Person>() {
int count = size;
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public Person next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
count--;
return PersonGenerator.this.next();
}
};
}
@Override
public Person next() {
try {
return (Person) types[rand.nextInt(1)].newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
/**
* 需要生成对象的类
* @author Alan
*
*/
class Person{
private static int counter = 0;
private final int id = counter++;
@Override
public String toString() {
return "Person [id=" + id + "]";
}
}
class Teacher extends Person { }
class Student extends Person { }
public class Test {
public static void main(String[] args) {
PersonGenerator p = new PersonGenerator(10);
for(Person s : p) {
System.out.println(s);
}
}
}
Java中的泛型无法使用基本类型,如int
类型在泛型中必须使用Integer
,Java SE5 中的自动装箱拆箱使得我们在使用时可以直接使用int
,例如下面这个生成斐波那契数列的类,同样是生成器的一个实现。
public class Fibonacci implements Generator<Integer> {
private int count = 0;
public Integer next() {
return fib(count++);//fib方法返回值为int,此处通过自动装箱将int转换为Integer
}
private int fib(int n) {
if(n < 2) {
return 1;
}
return fib(n-1) + fib(n-2);
}
public static void main(String[] args) {
Fibonacci f = new Fibonacci();
for(int i=0; i< 10; i++) {
System.out.println(f.next());
}
}
}
泛型也可以作用于方法上,并且泛型方法与其所在的类并没有关系,也就是说不管类是不是泛型的,其中的方法都可以是泛型的,创建一个泛型方法只需要在返回参数前声明类型参数即可,如下:
public class Test {
public <T> T fun(T obj) {
System.out.println(obj);
return obj;
}
}
值得注意的是,在一个泛型类中的静态方法是不能使用泛型类的类型参数的,因此静态类需要使用泛型能力,那么必须使其成为泛型类。
在使用泛型方法时,方法返回的类型会根据接收对象类型进行判断,但是如果没有接收对象而是将返回值作为参数传递给其他方法,那么参数推断就会失效,这时候必须显示声明返回类型。
class Person{}
class Pet{}
class ListUtil {
public static <T> List<T> list() {
return new ArrayList<T>();
}
}
public class Test{
public void fun(ArrayList<Integer> list) {
for(Integer i : list) {
System.out.println(i);
}
}
public static void main(String[] args) {
List<String> list = ListUtil.list();//具有参数推断,无需声明返回类型
fun(ListUtil.<Integer>>list());//需要声明返回类型
}
}
利用静态泛型方法,我们可以编写一个所有类型通用的生成器。
public class BasicGenerator<T> implements Generator<T> {
Class<T> type;
public BasicGenerator(Class<T> type) {
this.type = type;
}
@Override
public T next() {
try {
return type.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static <T> BasicGenerator<T> create(Class<T> type) {
return new BasicGenerator<T>(type);
}
}
class Person{
private static int count=0;
private int id = count++;
public String toString(){
return id+" ";
}
}
public class Test{
public static void main(String[] args) {
BasicGenerator<Person> gen = BasicGenerator.create(Person.class);
for(int i=0; i<10; i++) {
System.out.println(gen.next());
}
}
}
泛型同样可以应用于内部类以及匿名内部类中,还是以生成器接口为例。
interface Generator<T> {
T next();
}
class Custom {
private static int count = 1;
private int id = count++;
private Custom(){}//构造函数为私有,在其他类中无法直接new对象
public String toString() {
return "Custom " + id;
}
public static Generator<Custom> generator() {
return new Generator<Custom>() {
public Custom next() {
return new Custom();
}
};
}
}