泛型,顾名思义,广泛的使用呗,所以不确定用到的是哪一块了,是具体的哪一个东西,给你一个范围一个东西自己去选
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。注意这里的类型只能时引用类型,不能时基本数据类型。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型类
public class Generic< Q > {
private T key;
public Generic(Q key) { //泛型构造方法形参key的类型也为Q,Q的类型由外部指定
this.key = key;
}
}
就这样的,很简单,用“<”和“>”包起来了一个Q泛型,这个Q是什么,就是我们再创建类时具体传进去的类
比如:
Generic< String > newQ = new Generic< String >();这样的传进去了一个Stirng对象。没什么好说的了,就基本就是这样。
常用的java集合中,就用到了许多泛型的知识
Person person = new Student(3, “df”);
person.s();
ArrayList< Person > list1 = new ArrayList< Person >();
list1.add(new Person(3));
list1.add(new Person(4));
ArrayList< Student > list2 = new ArrayList< Student >();
list2.add(new Student(3, “f”));
list1.addAll(list2);
ArrayList< faslkf > list3 = new ArrayList< faslkf >();
list3.add(new faslkf());
list1.addAll(list3);
String[] strings = new String[] {“a”,“b”,“c”};
List< String > bList = Arrays.asList(strings);
bList.add(“d”);
System.out.println(bList);
下面这个例子中list的addAll方法,原型如下在参数中,用到了E泛型,其中?extends E表示任何继承E的子类都可以
泛型接口
泛型接口和泛型类基本都差不多,就像抽象类和接口有什么区别对吧,自己想一想就知道,顺便提一下,抽象类和接口的区别也是面试常常问的题目,上半年我面了三家,两家这么问的。
public interface Generator< T> {
public T next();
}
泛型接口使用有两种比较常用的方式一
class FruitGenerator< T> implements Generator< T>{
public T next() {
return null;
}
}
这里我们直观看过去并不知道T的具体类型,所以在创建对象的时候,必须传入一个具体的类型来告诉编译器你是啥。二
public class FruitGenerator implements Generator< String> {
private String[] fruits = new String[]{“Apple”, “Banana”, “Pear”};
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
这个就是在类的定义的时候直接将String类型传给了接口,所以我们在创建对象时就不需要进行七七八八的传入操作了。其实最常用的应该也是第二种了,因为所谓接口,就是提供给各种实现类去实现,那么接口用泛型很OK,但是对于实现类,应该还是要针对一种具体的情况去分类实现,这也是编程中一种比较艺术的方式,如果把各种类型都集中到一个类实现,就像第一种,那就要求各种类型的业务要求有很高的集中度和相似度,才能够降低成本,不然,还是要针对各种类型去实现。所以在开发中,一般情况还是用的第二种。
泛型方法
先来看一个模板
/**
泛型方法的基本介绍
@param tClass 传入的泛型实参
@return T 返回值为T类型
说明:
1)public 与 返回值中间< T>非常重要,可以理解为声明此方法为泛型方法。
2)只有声明了< T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
3)< T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public < T> T genericMethod(Class< T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
怎么说呢,不符合这种方式的都不是泛型方法
比如看看下面这种
public T a(){
}
这种只是引用了类中泛型T,并不是方法自己的泛型,你看类它用了自己的泛型,所以方法也应该用自己的泛型,而且再泛型类中,泛型方法可以和泛型类使用同一名字泛型
同时注意,这里有个比较特殊的方法,我们需要注意,就是静态方法,并不需要创建相应类的对象,我们研究静态方法的泛型的时候,也不需要类的信息,也根本不可能直到类的信息,所以,对于静态方法,我们不能使用类中的泛型和类定义的信息,比如下面
public class gei<T>{
public static T ik(){//这里根本不能这样使用,因为再类加载的时候这个静态方法就加载进了虚拟机,再这种情况下,类根本还没有创建对象,所以根本没有通过创建对象传入相应的实际参数类,这时候静态方法根本不知道T是啥,所以静态方法不能使用类中的泛型。
}
}
第二种情况:正确的静态方法使用情况
public static<T> T t() {
return t();
}
当然这个例子会内存溢出,这里只是为了举个例子而已,定义正确的静态方法泛型就是这样的。