目录
1. java 方法返回值中使用泛型
public class FooService<T> {
// 使用泛型返回
public <T> T getFoo() {
Foo foo = new Foo();
foo.setId(1);
foo.setName("1");
return (T) foo;
}
// 使用Object 直接返回
public Object getFoObject() {
Foo foo = new Foo();
foo.setId(2);
foo.setName("2");
return foo;
}
public static void main(String[] args) {
FooService<Foo> my = new FooService<Foo>();
Foo foo1 = my.getFoo();
System.out.println(foo1.getName());
Foo foo2 = (Foo) my.getFoObject();
System.out.println(foo2.getName());
}
}
使用泛型后 getFoo 方法的返回值不需要强制类型转换,避免了发生
ClassCastException
异常,编译时更安全,因为泛型是再运行时才确定类型,而并非再编译时确定。
所以再使用泛型后,可以再同一方法灵活的实现返回值类型。
2. 定义泛型类、接口
定义任意泛型的类、接口,只要在定义它们时用<>来指定类型参数即可。
例如:public class Fruit<T> { … },其中<T>指定了该泛型的类型参数,这个T是一个类型参数名,用户可以任意命名(就像方法参数的形参名一样),只有在使用该泛型的对象时将T替换成指定的具体类型从而产生一个实例化的泛型对象,例如:Fruit<String> fruit = new Fruit<>(…);
类型形参可以在整个接口、类体内当成普通类型使用,集合所有可使用普通类型的地方都可以使用类型形参,例如
public interface MyGneric<E> {
E add(E val);
Set<E> makeSet();
...
}
定义在<> 中的泛型的类型参数在定义他的类、接口里面就可以像普通类一样使用。
定义泛型构造器:泛型的构造器还是类名本身,不用使用菱形语法,定义构造器无需MyGeneric<T>(…) { … }了,只有在new的时候需要用到菱形语法;
public class MyGenric<T> {
MyGeneric(...) { ... }
...
}
实现/继承泛型接口/泛型类:
泛型也是在定义的时候必须使用形参(虚拟参数,用户自己随意命名),但是在使用泛型的时候(比如定义泛型引用、继承泛型)就必须使用实参,而泛型的实参就是具体的类型,像String、Integer等具体的类型(当然也可以是自定义类型)。Java还支持一种特殊的语法,可以让你从泛型继续派生出泛型,而泛型的类型参数可以继续传承下去;
class Father<T> { ... }
class Son<T> extends Father<T> { ... }
这个具体可以看 JDK 的 List 类,Collection 相关接口都是这么设计的。 在编译器中,是无法知道K和V具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。具体可以参考Map 接口的设计。
public class Container <K, V>{
private K key;
private V value;
public Container(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public static void main(String[] args) {
Container<String, String> container = new Container<String, String>("1", "2");
System.out.println(container.getKey());
}
3. 定义泛型方法
定义泛型方法需要在返回值前加入 <T> 来声明这是一个泛型方法。当然返回值也可以不是T。如果在类名旁已经定义了<T>, 也可以直接在方法里返回 T ,不需要再方法的返回值前加入<T> 也可以使用。
public class FooService {
public <T> T getFoo() {
Foo foo = new Foo();
foo.setId(1);
foo.setName("1");
return (T) foo;
}
}
public class FooService<T> {
public T getFoo() {
Foo foo = new Foo();
foo.setId(1);
foo.setName("1");
return (T) foo;
}
}
Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,作为Class 类型的对象,c 就可以拥有Class 对象的方法,可以根据反射来创建泛型类的对象。
public class FooService<T> {
public T getT(Class<T> t) throws InstantiationException, IllegalAccessException {
T a= t.newInstance();
return a;
}
}
4. 泛型字母规范
- E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
- K,V — Key,Value,代表Map的键值对
- N — Number,数字
- T — Type,类型,如String,Integer等等
字母是没有特定意义的!只是为了提高可读性
5. Class<T>、T 、T.class 的区别
单独的T 代表一个类型 ,而 Class<T>代表这个类型所对应的类, Class<?>表示类型不确定的类。
6. 如何创建一个 Class<T> 类型的实例,
可以通过 Class.forName() 或者使用类常量 X.class 。 Class.forName() 被定义为返回 Class<?> 。 另一方面,类常量 X.class 被定义为具有类型 Class<X> , 所以 String.class 是 Class<String> 类型的。
7. 方法中为什么需要 <T> T 修饰
泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前。其中第一个<T>是与传入的参数Class<T>相对应的,相当于返回值的一个泛型,后面的T是返回值类型,代表方法必须返回T类型的(由传入的Class<T>决定)