之前对泛型就是停留在会使用而已,今天把泛型的一些重要的例子记录一下以及对深层次的理解一下。
泛型小例子:
package zsc.lian.test;
import java.lang.reflect.InvocationTargetException;
import java.text.Annotation;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CollectionTest {
public static void main(String[] args) throws IllegalArgumentException,
SecurityException, IllegalAccessException,
InvocationTargetException, NoSuchMethodException {
/*
* 错误一 ,道理很直观,从编译器的角度上来说li原本是可以装任意类型的数据,
* 但是在实际内存可以装整型的数据,这样就产生了矛盾。因为在后面取出数据
* 时,就只可以取出整型类型的数据,但是我们规定却可以取出任意类型的数据, 所以矛盾,在编译期间就会报错
*/
// Mylist<Object> li = new Mylist<Integer>();
// 纠正Mylist<Object> li = new Mylist();
/*
* 错误二,道理仍然很直观,、从编译器角度来说li可以装整型的数据,但是实际上
* 在内存可以存放任意类型的数据,那么当取出数据时,取出的数据就什么类型都可以,
* 但是我们规定只能取出Integer类型的数据,所以就矛盾,编译就不通过。
*/
// Mylist<Integer> li = new Mylist<Object>();
// 纠正Mylist<Integer> li = new Mylist();
/*
* 错误三,不能使用这种List<String>[] lsa = new ArrayList<String>[10];
* List<String>[] lsa = new ArrayList<String>[10]; // illegal Object[]
* oa = lsa; // OK because List<String> is a subtype of Object
* List<Integer> li = new ArrayList<Integer>(); li.add(new Integer(3));
* oa[0] = li; String s = lsa[0].get(0); 最后一行将抛出
* ClassCastException,因为这样将把 List<Integer>填入本应是 List<String>的位置。
* 因为数组协变会破坏泛型的类型安全,所以不允许实例化泛型类型的数组(除非类型参数是未绑定的通配符,比如 List<?>)
*/
// 纠正List<?>[] lsa = new List<?>[3];
/*
* 第一种方式,利用这种骗过编译器
*/
Mylist ii = new Mylist<Integer>();
Mylist<Object> mylist = ii;
mylist.add("33");
mylist.PrintVaule();
/*
* 第二种方式,利用反射绕过编译器
*/
Mylist<Integer> li1 = new Mylist<Integer>();
li1.getClass().getMethod("add", Object.class).invoke(li1, "adf");
li1.PrintVaule();
printMyList(li1);
// 通配符扩展
/*
* 限定通配符的上边界
*/
Mylist<? extends Number> x = new Mylist<Integer>();// right,因为Integer继承了Number
// Mylist<? extends Number> x1 = new Mylist<String>(); wrong
/*
* 限定通配符的下边界
*/
Mylist<? super Integer> x2 = new Mylist<Number>();// right,因为Number是Integer的父类
// Mylist<? super String> x3 = new Mylist<Number>();wrong
try {
Class<?> c = Class.forName("java.lang.String");
System.out.println(c);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* 将父类的class对象强制转成子类对象。不能是其他对象,必须是子类的
*/
Class<? extends Number> cs = Integer.class.asSubclass(Number.class);
System.out.println(cs.getCanonicalName());
A a = new A();
System.out.println(a.getClass().getName());
Class<? extends A> cs1 = B.class.asSubclass(A.class);
System.out.println(cs1.getName());
//调用change方法
change(new String[]{"asdfa","sdfas","sdfe"},1,2);
}
/*
* 报错四,原因见上面错误一二 public void printMyList(Mylist<Object> li1){
*
* }
*/
/*
* 报错五,因为不知传入过来的是什么类型,万一传入一个Mylist<Integer> 的呢?那么我们就无法li1.add("asd") public
* static void printMyList(Mylist<?> li1){ li1.add("adfd"); }
*/
//解决方法
public static <T> void printMyList1(Mylist<T> list,T obj){
list.add(obj);
}
/*
* ?号是通配符,可以指向任意类型
*/
public static void printMyList(Mylist<?> li1) {
li1 = new Mylist<Date>();
}
/*
* 定义类似C++模版,对任意类型的数组交换位置
*/
public static <T> void change(T[] array,int i,int j){
T k = array[i];
array[i] = array[j];
array[j] = k;
}
}
class A{
}
class B extends A{
}
class C extends A{
/*
* 只能返回A的子类,返回其他类型报错
*/
public <Y extends A> Y getMyA(Class<Y> A){
return (Y) new B();
}
}
class Mylist<I> {
Object o;
Object o1;
public Mylist() {
}
public Mylist(I i) {
o = i;
System.out.println(o);
}
public void add(I e) {
o1 = e;
}
public void PrintVaule() {
System.out.println(o1.getClass().getName());
}
}
泛型例子二:
package zsc.lian.test;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;
//class GenericDao {
// public <T> void save(T t){
//
// }
//
// public <T> T findId(int id){
// return null;
// }
//}
//解决方法
class GenericDao<T> {
public void save(T t){
}
public T findId(int id){
return null;
}
public static void applyVector(Vector<Date> v){
}
//错误,静态方法不能这样使用泛型
// public static void update(T obj){
//
// }
}
class Person{
}
public class Test{
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
/*
* 插入和返回的数据类型不一致,即添入是什么
* 类型的数据,取出应该就是什么类型的数据
*/
GenericDao<Person> Gd = new GenericDao<Person>();
Gd.save(new Person());
Person s = Gd.findId(1);
for (int i = 0; i < 10; i++) ; //为了打印StackMapTable信息
Method method = Gd.getClass().getMethod("applyVector",Vector.class);
Type[] type = method.getGenericParameterTypes();
ParameterizedType pt = (ParameterizedType) type[0];
System.out.println(pt.getActualTypeArguments()[0]);
}
}
之前对一直对这个例子很迷惑,为什么会迷惑,因为,总所周知,泛型只是做给编译器看的而已,也就是说由编译器编译后泛型信息会被擦除,class文件不会保存泛型的信息。那么上面那个例子GenericDao类中怎么可以通过反射来确定Vector可以装Date类型的数据呢?难道并且没有擦除,于是用了JAD反编译一下查看到了代码,
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: Test.java
package zsc.lian.test;
import java.util.Vector;
class GenericDao
{
GenericDao()
{
}
public void save(Object obj)
{
}
public Object findId(int id)
{
return null;
}
public static void applyVector(Vector vector)
{
}
}
查看到GenericDao.Class文件中确实没有保存泛型的信息,确实是被擦除了。那原因是处在哪里呢?请教了几个工作多年的朋友,也没有得到很好的回答。后来在网上搜到一篇不错的文章:http://rednaxelafx.iteye.com/blog/586212
里面说道:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
具体可以查看此文章。
确实,在java 5,Class文件中确实加入了泛型的信息。但是只是在声明一侧加入了。
查看Test.Class的数据类型:
StackMapTable: number_of_entries = 2
frame_type = 254 /* append */
offset_delta = 33
locals = [ class zsc/lian/test/GenericDao, class zsc/lian/test/Person, int ]
frame_type = 2 /* same */
从上面看泛型信息被擦除了,接着看看GenericDao.Class的信息
public static void applyVector(java.util.Vector<java.util.Date>);
flags: ACC_PUBLIC, ACC_STATIC
Signature: #30 // (Ljava/util/Vector<Ljava/util/Date;>;)V
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 31: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 v Ljava/util/Vector;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 1 0 v Ljava/util/Vector<Ljava/util/Date;>;
}
既然保存了泛型的信息,那么就可以通过反射获取泛型的具体类型。
分享到:
相关推荐
Java泛型是Java语言的一个重要特性,它在Java SE 5.0中被引入,极大地提高了代码的类型安全性和重用性。泛型允许在定义类、接口和方法时使用类型参数,使得这些构造能够在多种不同的数据类型上工作,而无需进行强制...
### 泛型全记录 #### 一、泛型概述 泛型是Java 5.0引入的一个重要特性,它的出现极大地改善了Java编程语言的类型安全性,并提供了代码重用性。通过使用泛型,开发者可以在类、接口或方法中定义类型参数,这样就...
Page类是用来表示分页结果的,它通常包含当前页数、总页数、每页记录数、总记录数以及实际的数据集合。Page类提供了方便的方法来获取和设置这些属性,以及计算和定位页码,使得在业务逻辑中处理分页变得简单。 然后...
### Java使用泛型详解 #### 一、泛型的基本概念 在Java中,泛型是一种在编译时检查类型安全性的机制,它允许我们在类、接口或方法中使用类型参数来替代具体的类型。通过使用泛型,我们可以编写更加灵活、可重用性...
在Java编程语言中,反射和泛型是两个非常重要的特性,它们在开发复杂和灵活的应用程序时发挥着关键作用。本资源"反射泛型完美版分页.rar"似乎提供了一个结合了这两种特性的分页解决方案,特别适用于处理大量数据的...
- log4j.jar:日志处理库,提供灵活的日志记录机制,便于调试和问题追踪。 - commons-lang3.jar:Apache Commons Lang,包含各种通用的字符串、数学和日期处理函数。 - poi.jar:Apache POI,用于读写Microsoft ...
- **理解WSDL与Java泛型的差异**:在进行开发之前,需要深入了解WSDL与Java泛型之间的差异,这对于正确设计数据模型至关重要。 - **利用工具辅助**:可以考虑使用一些成熟的工具或框架,如Apache Axis等,这些工具...
Java加强课程涵盖了许多核心概念,其中包括反射、代理、泛型以及BeanUtils的使用。这些是Java编程中的关键技能,能够帮助开发者实现更高效、灵活的代码。以下是对这些主题的详细解释: 1. 反射(Reflection): ...
结构体是值类型,通常用于存储小量的数据,如坐标、尺寸或简单的记录。在泛型上下文中,我们可以创建一个通用的结构体,该结构体可以适应多种不同的数据类型。 在.NET框架中,C#支持泛型结构体。例如,`System....
4. **执行SQL**:使用Java的JDBC API执行SQL,处理结果集,并将数据转换为Java对象,或将Java对象转换为数据库记录。 5. **泛型接口**:创建泛型的DAO接口,如`GenericDAO<T>`,其中T代表任何实现了ORM注解的实体类...
总结起来,将Map转换为Java实体对象是Java开发中常见的需求,我们可以利用Core Java JDK 1.8的泛型和反射来实现这一功能。通过创建一个通用的工具类,我们可以简化代码并提高代码复用性。在使用过程中,需要注意处理...
项目中碰到的,记录一下
firstApplet.java 第一个用Java开发的Applet小程序。 firstApplet.htm 用来装载Applet的网页文件 第2章 示例描述:本章介绍开发Java的基础语法知识。 accumulationByDoWhile.java 用do~while语句写的累加程序 ...
项目中碰到的,记录一下
9. **版本控制**:虽然未在描述中提及,但良好的开发实践中,项目通常会使用Git等版本控制系统来管理代码,记录每次修改并协作开发。 通过研究这个源码,开发者不仅可以加深对Java语言的理解,还能掌握到Android...
本教程“JavaTutorial”涵盖了Java编程语言的一些关键方面,包括多线程、泛型、反射、输入/输出(IO)以及容器类和注解。下面将对这些主题进行详细阐述。 首先,多线程是Java的一个强大特性,它允许程序同时执行多...
Java泛型类型是一种类型参数,它可以在编译时检查类型的正确性。使用Java泛型类型可以提高代码的可读性和可维护性。 Berkeley DB Java 版本提供了一个强大的API来访问数据库。API提供了一个统一的接口来访问数据库...