java泛型 class<T>
2023-03-17 22:30 youxin 阅读(234) 评论(0) 编辑 收藏 举报泛型中通配符
我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?
常用的 T,E,K,V,?
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:
- ?表示不确定的 java 类型
- T (type) 表示具体的一个java类型
- K V (key value) 分别代表java键值中的Key Value
- E (element) 代表Element
泛型的通配符
不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
其中泛型通配符还可以这样使用:
-
<? extends Person>:表示可以传递Person及其子类
-
<? super Person>:表示可以传递Person及其父类
注意:
-
泛型不存在继承关系:ArrayList<Object>list并不是ArrayList<String>list1和ArrayList<Integer>list2的父类,它们三个是三个不同的类型。
-
其中Java里的泛型是一种伪泛型。
什么叫伪泛型?
也就是泛型只存在于编译时期,在运行时期会被擦除,这个比较抽象不好说明,了解就好了。真泛型也是有的,但在Java语言里没有,C语言里有。
上界通配符 < ? extends E>
上界:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:
- 如果传入的类型不是 E 或者 E 的子类,编译不成功
- 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
private <K extends A, E extends B> E test(K arg1, E arg2){
E result = arg2;
arg2.compareTo(arg1);
//.....
return result;
}
类型参数列表中如果有多个类型参数上限,用逗号分开
下界通配符 < ? super E>
下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object
在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。
private <T> void test(List<? super T> dst, List<T> src){
for (T t : src) {
dst.add(t);
}
}
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = new ArrayList<>();
new Test3().test(animals,dogs);
}
// Dog 是 Animal 的子类
class Dog extends Animal {
}
dst 类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src 。
?和 T 的区别
?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行,比如如下这种 :
// 可以
T t = operate();
// 不可以
?car = operate();
简单总结下:
T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
https://zhuanlan.zhihu.com/p/378411807
如何创建一个Class<T>类型的实例?
就像使用非泛型代码一样,有两种方式:调用方法 Class.forName() 或者使用类常量X.class。 Class.forName() 被定义为返 回 Class<?>。另一方面,类常量 X.class 被定义为具有类型 Class<X>,所 以 String.class 是Class<String> 类型的。
三、方法中为什么需要<T> T修饰呢
泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前。
public static <T> T request2Bean(HttpServletRequest request,Class<T> clazz){}
其中第一个<T>是与传入的参数Class<T>相对应的,相当于返回值的一个泛型,后面的T是返回值类型,代表方法必须返回T类型的(由传入的Class<T>决定
在Java中,每个 class 都有一个相应的 Class 对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个 Class 对象,用于表示这个类的类型信息
获取 Class 实例的三种方式:
- (1) :利用 对象调用 getClass()方法 获取该对象的Class实例;
- (2) :使用 Class类的静态方法 forName(),用类的名字获取一个Class实例(staticClass forName(String className) Returns the Classobject associated with the class or interface with the given stringname. );
- (3) :运用 .class 的方式来获取 Class 实例,对于基本数据类型的封装类,还可以采用 .TYPE 来获取相对应的基本数据类型的Class实例
在 newInstance() 调用类中缺省的构造方法 ObjectnewInstance()(可在不知该类的名字的时候,创建这个类的实例) Creates a new instance of the class represented by this Classobject.
在运行期间,如果要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到 .class 文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象
Class对象的生成方式如下:
1. Class.forName("类名字符串") (注意:类名字符串必须是全称,包名+类名)。
2. 类名.class ( Object.class )。
3. 实例对象.getClass() (object.getClass() )。
五、Object.class 和 instanceObj.getClass() 的区别
两者的区别如下:
- 类名.class 叫做 "类字面量",因 class 是关键字, 所以 类名.class 编译时确定。
- 而 getclass() 是某个具体的方法来调用,是运行时根据实际实例确定,getClass()是动态而且是 final 的。
例如:
- String.class 是能对类名的引用取得在内存中该类型class对象的引用,
- 而 new String().getClass() 是通过实例对象取得在内存中该实际类型class对象的引用。
public class Demo<T extends Animal> { private T ob; public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } public Demo(T ob) { super(); this.ob = ob; } public void print() { System.out.println("T的类型是:" + ob.getClass().getName()); } }
public class Demotest { private static void take(Demo<?> a){ a.print(); } public static void main(String[] args) { Demo<Dog> dog=new Demo<Dog>(new Dog()); take(dog); Demo<Cat> cat=new Demo<Cat>(new Cat()); take(cat); Demo<Animal> animal=new Demo<Animal>(new Animal()); take(animal); } }
CLASS
在Java中,每个 class 都有一个相应的 Class 对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个 Class 对象,用于表示这个类的类型信息
获取 Class 实例的三种方式:
- (1) :利用 对象调用 getClass()方法 获取该对象的Class实例;
- (2) :使用 Class类的静态方法 forName(),用类的名字获取一个Class实例(staticClass forName(String className) Returns the Classobject associated with the class or interface with the given stringname. );
- (3) :运用 .class 的方式来获取 Class 实例,对于基本数据类型的封装类,还可以采用 .TYPE 来获取相对应的基本数据类型的Class实例
在 newInstance() 调用类中缺省的构造方法 ObjectnewInstance()(可在不知该类的名字的时候,创建这个类的实例) Creates a new instance of the class represented by this Classobject.
在运行期间,如果要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到 .class 文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象
Class对象的生成方式如下:
1. Class.forName("类名字符串") (注意:类名字符串必须是全称,包名+类名)。
2. 类名.class ( Object.class )。
3. 实例对象.getClass() (object.getClass() )。