`
jjchen_lian
  • 浏览: 84472 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

java泛型小记录

    博客分类:
  • java
阅读更多

之前对泛型就是停留在会使用而已,今天把泛型的一些重要的例子记录一下以及对深层次的理解一下。
泛型小例子:

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开发技术大全(500个源代码).

    firstApplet.java 第一个用Java开发的Applet小程序。 firstApplet.htm 用来装载Applet的网页文件 第2章 示例描述:本章介绍开发Java的基础语法知识。 accumulationByDoWhile.java 用do~while语句写的累加程序 ...

    泛型、TreeMap.rar

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。

    java数据结构,算法,爬虫,泛型,反射等实现

    android,java必备知识,面试知识,工作学习记录。这里记录一些常用android工具类,android开发经验,面试算法题,牛客算法题解析。也包含java数据结构,算法,爬虫,泛型,反射等实现.zip

    JAVA上百实例源码以及开源项目源代码

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    清华妹子的Java仓库(进阶学习路线)

    Java基础学习(3)——泛型 Java基础学习(4)——动态代理 《Java多线程核心技术》读书笔记 JDK源码 Java集合框架源码解读(1)——ArrayList、LinkedList和Vector Java集合框架源码解读(2)——HashMap Java集合框架...

    Java Web分页工具类

    2. pagedList 静态方法:这是一个泛型方法,接收总记录数、每页记录数、当前页码和一个包含所有记录的列表。它将基于这些参数创建一个 `PageInfo` 实例,计算当前页的记录范围,并返回只包含当前页记录的分页信息。 ...

    JAVA上百实例源码以及开源项目

     Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置...

    疯狂JAVA讲义

    第1章 Java概述 1 1.1 Java语言的发展简史 2 1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6...

    CodeHighlights:源代码集合,突出了我在各种相关软件和编程领域中的技术经验

    QuickSort.java-使用Java泛型实现的标准Quicksort。 Java,递归,泛型,排序BinarySearchTree.java-使用Java泛型的BST,包括许多理想的BST操作方法。 Java,OOP,泛型,搜索Sieve.c-实现Eratosthenes的Sieve,使用...

    java开源包4

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

    java开源包101

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

    java开源包11

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

    java开源包6

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

    java开源包9

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

    java开源包8

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

    java开源包10

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

    java笔试题算法-C5:用于C#/.NET的C5泛型集合库

    库提供了广泛的经典数据结构、丰富的功能、最佳的渐近时间复杂度、记录的性能和经过全面测试的实现。 C5 库的目标 总体目标是让 C5 成为 C# 编程语言和公共语言基础设施 (CLI) 的通用集合库,其功能、效率和质量满足...

    java开源包5

    一个Java的类库,用于异步输出记录的简单小框架用于高并发下数据输出使用。 Java转C++代码工具 J2C J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-...

Global site tag (gtag.js) - Google Analytics