知识点五 java5的注解
一、概述:
1、注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则没有某种标记。
2、以后,java编译器、开发工具和其他应用程序就可以用反射来了解自己的类及各种元素上有无何种标记,有什么标记,就会做出相应的处理。
3、标记可以加在包、类、字段、方法、方法参数,以及局部变量上等等。
4、在java.lang包中提供了最基本的annotation,即注解。
5、格式:@注解类名()。如果有属性,则在括号中加上属性名(可省略)和属性值。
二、java中三种最基本的注解:
1、@SuppressWarning(”deprecation”)--->压制警告
SupressWarning是告知编译器或开发工具等提示指定的编译器警告;
”deprecation”是告知具体的信息即方法已过时。
通过System.runFinalizersOnExit(true);的编译警告引出@SuppressWarnings("deprecation")
2、@Deprecated--->提示成员等已经过时,不再推荐使用。
源代码标记@Deprecated是在JDK1.5中作为内置的annotation引入的,用于表明类(class)、方法(method)、字段(field)已经不再推荐使用,并且在以后的JDK版本中可能将其删除,编译器在默认情况下检测到有此标记的时候会提示警告信息。
直接在刚才的类中增加一个方法,并加上@Deprecated标注,在另外一个类中调用这个方法。
例如:假定之前的某个类升级了,其中的某个方法已经过时了,不能够将过时的方法删除,因为可能会影响到调用此类的这个方法的某些程序,这是就可以通过在方法上加这个注解。
3、@Override--->提示覆盖(父类方法)
加上此注解,,可对自己类中的方法判断是否是要覆盖的父类的方法,典型的例子即在集合中覆盖equals(Object obj)方法,其中的参数类型必须是Object,才能被覆盖,若不是,加上此注解就会提示警告。
例如:public boolean equals(Reflect other)方法与HashSet结合
三、注释的应用--->注解类:
1、定义格式:@interface 名称{statement}
2、元注解(注解的注解)
一个注解有其生命周期(Retetion)和存放的位置(Taget),这就可以通过元注解说明。
1)Retetion:用于说明注解保留在哪个时期,加载定义的注解之上。
①一个注解的声明周期包含:
java源程序--(javac)-->class文件--(类加载器)-->内存中的字节码
第一、当再源程序上加了注解,javac将java源程序编译为class文件,可能会把源程序中的一些注解去掉,进行相应的处理操作,当我们拿到源程序的时候,就看不到这些注解了。
第二、假设javac把这些注解留在了源程序中(或者说留在了class文件中),当运行此class文件的时候,用类加载器将class文件调入内存中,此时有转换的过程,即把class文件中的注解是否保留下来也不一定。
注意:class文件中不是字节码,只有把class文件中的内部加载进内存,用类加载器加载处理后(进行完整的检查等处理),最终得到的二进制内容才是字节码。
②Reteton(枚举类)取值:
Retetion.Policy.SOURSE:java源文件时期,如@Overried和@SuppressWarning
Retetion.Policy.CLASS: class文件时期(默认阶段)
Retetion.Policy.RUNTIME:运行时期,如@Deprecated
2)Taget:用于说明注解存放在哪些成分上,默认值是任何元素
其值可设置为枚举类ElementType类中的任何一个,包括:包、字段、方法、方法参数、构造器、类等值。取值为:
PACKAGE(包声明)
FIELD(字段声明)
ANNOTATION_TYPE(注释类型声明)
CONSIRUCTOR(构造器声明)
METHOD(方法声明)
PARAMETER(参数声明)
TYPE(类、接口(包含注释类型)或枚举声明)
LOCAL_VARIABLE(局部变量声明)
注意:其中代表类的值是TYPE。因为class、enum、interface和@interface等都是属于Type的。不可用CLASS表示。
3、通过反射查看其它类中的注释:
过程:
第一、注解类:@interfaceA{}
第二、应用了“注释类”的类:@Aclass B{}
第三、对“应用注释类的类”进行反射操作的类:class{...},操作如下:
B.class.isAnnotionPresent(A.class);//判断是否存在此注解类
A a = B.class.getAnnotation(a.class);//存在的话则得到这个注释类的对象
注解的应用结构图:
示例:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface ItcastAnnotation {}
@ItcastAnnotation()
public class AnnotionTest {
@SuppressWarnings("deprecation")//表示压制警告的注解
@ItcastAnnotation()
public static void main(String[] args) {
System.runFinalizersOnExit(true);
//反射方式查看注解
//检查类上是否有注解
if(AnnotionTest.class.isAnnotationPresent(ItcastAnnotation.class)){
//通过反射获取到注解
ItcastAnnotation annotation = AnnotionTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation);
}
}
四、自定义注解及其应用
1)定义一个最简单的注解:public @interface MyAnnotation {}
2)把它加在某个类上:@MyAnnotation public class AnnotationTest{}
3)用反射进行测试AnnotationTest的定义上是否有@MyAnnotation
4)根据发射测试的问题,引出@Retention元注解的讲解,其三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应:java源文件-->class文件-->内存中的字节码。
思考:@Override、@SuppressWarnings和@Deprecated这三个注解的属性值分别是什么?
5)@Target元注解
Target的默认值为任何元素,设置Target等于ElementType.METHOD,原来加在类上的注解就报错了,改为用数组方式设置{ElementType.METHOD,ElementType.TYPE}就可以了。
元注解以及其枚举属性值不用记,只要会看jdk提供那几个基本注解的API帮助文档的定义或其源代码,按图索骥即可查到,或者直接看java.lang.annotation包下面的类。
五、为注解增加基本属性
1、属性:
一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是传智播客的学生,否则,就不是。如果还想区分出是传智播客哪个班的学生,这时候可以为胸牌在增加一个属性来进行区分。加了属性的标记效果为:@MyAnnotation(color="red")
2、定义格式:同接口中的方法一样:String color();
定义缺省格式:Stringvalue() default ”ignal”;
定义基本类型的属性和应用属性:
在注解类中增加String color();
@MyAnnotation(color="red")
3、应用:直接在注解的括号中添加自身的属性,如:
@ItcastAnnotation(color=”red”)
这个和上面的@SuppressWarnings("deprecation")是一样的,其中的"deprecation"就是属性值
1)当只有一个属性时,可直接传入属性值。如”red”
2)当含有其他属性值的时候,如果那个属性值是缺省的(default),也可以直接传入这个属性值。
4.用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象
5.为属性指定缺省值:
String color() default "yellow";
value属性:
String value() default "zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。
六、为注解增加高级属性
1、可以为注解增加的高级属性的返回值类型有:
1)八种基本数据类型 2)String类型 3)Class类型
4)枚举类型 5)注解类型 6)前五种类型的数组
2、数组类型的属性:
定义:int[]arrayArr() default {1,2,3}; -->可不定义默认值
应用:@MyAnnotation(arrayArr={2,3,4}) --> 可重新赋值
注:若数组属性中只有一个元素(或重新赋值为一个元素),这时属性值部分可省略大括号。
3、枚举类型的属性:
假设定义了一个枚举类TraffLamp,它是EnumTest的内部类,其值是交通灯的三色。
定义:EnumTest.TrafficLamplamp();
应用:@MyAnnotation(lamp=EnumTestTrafficLamp.GREEN)
4、注解类型的属性:
假定有个注解类:MetaAnnotation,其中定义了一个属性:String value()
定义:MetaAnnotationannotation() default @MetaAnnotation(”xxx”);
应用:@MyAnnotation(annotation=@MetaAnnotation(”yyy”)) --> 可重新赋值
可认为上面的@MetaAnnotation是MyAnnotation类的一个实例对象,同样可以认为上面的@MetaAnnotation是MetaAnnotation类的一个实例对象,调用:
MetaAnnotation ma =MyAnnotation.annotation();
System.out.println(ma.value());
5、Class类型的属性:
定义:Class cls();
应用:@MyAnnotation(cls=ItcastAnnotion.class)
注:这里的.class必须是已定义的类,或是已有的字节码对象
7、基本数据类型的属性(以int为例):
定义:int val()default 3; -->可不定义默认值
应用:@MyAnnotation(val=7) --> 可重新赋值
8、注解的详细语法可通过查看java语言规范了解即javaLanguage Specification
示例:
//自定义注解类
package cn.itcast.text2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cn.itcast.text1.EnumText;
//将定义的注解的生命周期设置在运行时期
@Retention(RetentionPolicy.RUNTIME)
//定义注解的放置位置
@Target({ElementType.TYPE,ElementType.METHOD})
//自定义注解
public @interface ItcastAnnotation {
//定义属性
String str();
int val() default 1;
int[] arr() default {2,3,4};
Class cls() default AnnotionTest.class;
EnumText.TrafficLamp lamp() default EnumText.TrafficLamp.YELLOW;
MetaAnnotation annotation() default @MetaAnnotation("sss");
}
//测试注解类,用反射查看其属性
package cn.itcast.text2;
import cn.itcast.text1.EnumText;
@ItcastAnnotation(annotation=@MetaAnnotation("anntation"),
Lamp=EnumText.TrafficLamp.RED,
arr=7,val=5,str="String",
cls=ItcastAnnotation.class)
public class AnnotionTest {
@SuppressWarnings("deprecation")//表示压制警告的注解
@ItcastAnnotation(str = "yyy")//有缺省值可不用写缺省部分
public static void main(String[] args) {
//反射方式查看注解
//检查类上是否有注解
if(AnnotionTest.class.isAnnotationPresent(ItcastAnnotation.class)){
//通过反射获取到注解
ItcastAnnotation annotation = AnnotionTest.class.getAnnotation(ItcastAnnotation.class);
//打印查看属性值
System.out.println(annotation);
System.out.println(annotation.str());
System.out.println(annotation.val());
System.out.println(annotation.arr().length);
System.out.println(annotation.cls().getName());
System.out.println(annotation.lamp().nextLamp());
System.out.println(annotation.annotation().value());
}
}
}
//定义枚举类,交通灯
package cn.itcast.text1;
public class EnumText {
public static void main(String[] args) {}
//定义交通灯
public enum TrafficLamp{
//定义3个元素,即此类的子类,覆写抽象方法
RED(30){
@Override
public TrafficLamp nextLamp() {return GREEN;}},
GREEN(45){
@Override
public TrafficLamp nextLamp() {return YELLOW;}},
YELLOW(5) {
@Override
public TrafficLamp nextLamp() {return RED;}};
private int time;
//构造方法
private TrafficLamp(int time){this.time = time;}
//抽象方法,转为下个灯
public abstract TrafficLamp nextLamp();
}
}
补充:
①枚举和注解都是特殊的类,不能用new 创建它们的实例对象,创建枚举的实例对象就是在其中增加元素。
在程序中如何创建出一个注解的实例对象啊?直接用@放上一个标记即可
MetaAnnotation注解的定义:
public @interface MetaAnnotation {
String value();
}
②看java语言规范:
打开j3toc.html页面后,搜索annotation,进入9.6节,在其中的第5个Discussion部分就有属性类型的讲解说明,可以知道枚举的属性类型包括:基本数据类型,String,Class,枚举,其他注解,以及这些类型的数组。
知识点六 java5的泛型
一 泛型概述
一、泛型的引出
1.Jdk 1.5以前的集合类中存在什么问题
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
int i = (Integer) collection.get(1);//编译要强制类型转换且运行时出错!
2.Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据
ArrayList<Integer> collection2 = new ArrayList<Integer>();
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*///这两行代码编译时就报告了语法错误
int i2 = collection2.get(0);//不需要再进行类型转换
3.泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
二、使用泛型的好处:
1、使用泛型集合,可将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象;这样就将运行时期出现的问题ClassCastException转移到了编译时期,方便与程序员解决问题,让运行时期问题减少,提高安全性。
2、当从集合中获取一个对象时,编译器也可知道这个对象的类型,不需要对对象进行强制转化,避免了强制转换的麻烦,这样更方便。
三、泛型格式:
通过<>来定义要操作的引用数据类型
如:ArrayList<String> -----> 来定义要存入集合中的元素指定为String类型
四、ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化:parametered,已经将参数变为实际类型的状态。
五、参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如, Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?
原始类型可以引用一个参数化类型的对象,编译报告警告,例如, Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去
六、参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
Vector<Object> v = new Vector<String>(); //也错误!
解释:泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection<String>和Collection<Object>是两个没有转换关系的参数化的类型。
假设Vector<String> v = new Vector<Object>();可以的话,那么以后从v中取出的对象当作String用,而v实际指向的对象中可以加入任意的类型对象;假设Vector<Object> v = new Vector<String>();可以的话,那么以后可以向v中加入任意的类型对象,而v实际指向的集合中只能装String类型的对象。
举例:咱们90的跟70的谈事,可以不按90的规则走,但是跟90谈,咱们必须按90的套路来。
七、编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];
思考题:下面的代码会报错误吗?
Vector v1 = new Vector<String>();
Vector<Object> v = v1;
答:这两句代码不会报错,因为参数化类型与原始类型相互兼容,应该按照java编译方式来推,而不应该以java执行的代码来推;
示例:
import java.lang.reflect.Constructor;
import java.util.*;
public class Generic {
public static void main(String[] args) throws Exception {
ArrayList<String> al = new ArrayList<String>();
al.add("25");
al.add("b");
System.out.println(al.get(1));
ArrayList<Integer> at = new ArrayList<Integer>();
at.add(23);
at.add(3);
System.out.println(at.get(1));
//编译器生成的字节码会去掉泛型的类型信息
System.out.println((al.getClass() == at.getClass()) +
"-->" + at.getClass().getName());
//at.add("ab")-->报错,存储的应为Integer类型
//反射方式,由于编译器生成的字节码会去掉泛型的类型信息,
//所以用反射可跳过编译器,存入任何类型
at.getClass().getMethod("add",Object.class).invoke(at,"abcd");
at.getClass().getMethod("add",Object.class).invoke(at,5);
System.out.println("反射方式:" + at.get(3));
System.out.println("反射方式:" + at.get(4));
//反射方式获得new String(new StringBuffer("abc"));
Constructor<String> cons = String.class.getConstructor(StringBuffer.class);
String st = cons.newInstance(new StringBuffer("abc"));
System.out.println(st);
二 泛型中的?通配符
一、问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection<Object> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
/* cols.add("string");//没错
cols = new HashSet<Date>();//会报告错误!*/
}
正确方式:
public static void printCollection(Collection<?> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet<Date>();
}
二、总结:
1.使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
2.当传入的类型不确定时,可以使用通配符?
3.可对通配符变量赋任意值:
如:Collection<?> colls---> cols = newHashSet<Date>();
Cols<Object> 中的Object只是说明Cols<Object> 实例对象中的方法接受的参数是Object
Cols<Object> 是一种具体类型,new HashSet<Date>也是一种具体类型,两者没有兼容性问题。
注意:
ollection<?> a可以与任意参数化的类型匹配,但到底匹配的是什么类型,只有以后才知道,所以,
a=new ArrayList<Integer>和a=new ArrayList<String>都可以, 但a.add(new Date())或a.add(“abc”)都不行
示例:
public static void printObj(Collection<?> coll){
//coll.add(1);是错误的,如果传入的是String类型,就不符合了
for(Object obj : coll){
System.out.println(obj);
}
}
import java.util.*;
class GenerticDemo
{
public static void main(String[] args)
{
ArrayList<String> p = new ArrayList<String>();
p.add("per20");
p.add("per11");
p.add("per52");
print(p);
ArrayList<Integer> s = new ArrayList<Integer>();
s.add(new Integer(4));
s.add(new Integer(7));
s.add(new Integer(1));
print(s);
}
public static void print(ArrayList<?> al) {
Iterator<?> it = al.listIterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
三、泛型中的?通配符的扩展
1.限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
2.限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
提示:
3.限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。
示例:
/*
泛型的限定:
*/
import java.util.*;
class GenerticXian2
{
public static void main(String[] args)
{
TreeSet<Student> s = new TreeSet<Student>(new Comp());
s.add(new Student("stu0"));
s.add(new Student("stu3"));
s.add(new Student("stu1"));
print(s);
System.out.println("Hello World!");
TreeSet<Worker> w = new TreeSet<Worker>(new Comp());
w.add(new Worker("Worker0"));
w.add(new Worker("Worker3"));
w.add(new Worker("Worker1"));
print(w);
}
public static void print(TreeSet<? extends Person> ts) {
Iterator<? extends Person> it = ts.iterator();
while (it.hasNext()){
Person p = it.next();
System.out.println(p.getName());
}
}
}
class Person implements Comparable<Person> {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int compareTo(Person p){
return this.getName().compareTo(p.getName());
}
}
class Comp implements Comparator<Person> {
public int compare(Person p1,Person p2){
return p1.getName().compareTo(p2.getName());
}
}
class Student extends Person {
Student(String name){
super(name);
}
}
class Worker extends Person {
Worker(String name){
super(name);
}
}
三 泛型方法
一、java中泛型方法的定义:
1、何时定义泛型方法:为了让不同方法可以操作不同的类型,而且类型不确定,那么就可以定义泛型方法
2、特殊之处:静态方法不可以访问类上定义的泛型,如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上
private static <T> T add(T a, T b){......return null;
}
add(3,5);//自动装箱和拆箱Number x1 = add(3.5,5);//取两个数的交集类型NumberObject x2 = add(3,"abc");//去最大交集为Object
二、泛型方法的特点:
1、位置:用于放置泛型的类型参数的<>应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前,按照惯例,类型参数通常用单个大写字母表示。
2、只有引用类型才能作为泛型方法的实际参数
3、除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符。
4、普通方法、构造函数和静态方法中都可以使用泛型。
5、可以用类型变量表示异常,称之为参数化的异常,可用于方法的throws列表中,但是不能用于catch子句中。
6、在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开。
public static <K,V> V getValue(K key){
Map<K, V> map = new HashMap<K, V>();
return map.get(key);
}
private static <T extends Exception> void sayHello() throws T{
try{}
catch(Exception e){
throw (T)e;
}
}
三、这个T和?有什么区别呢?
1、T限定了类型,传入什么类型即为什么类型,可以定义变量,接收赋值的内容。
2、?为通配符,也可以接收任意类型但是不可以定义变量。
但是这样定义,虽然提高了扩展性,可还是有一个局限性,就是不能使用其他类对象的特有方法。
3、总结:
通配符方案要比泛型方法更有效,当一个类型变量用来表达两个参数之间或参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用,而不是仅在签名的时候使用,才需要使用泛型方法。
四 泛型类
一、概述:
1、若类实例对象中多出要使用到同一泛型参数,即这些地方引用类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型。
2、何时定义泛型类:当类中要操作的引用数据类型不确定时,在早期定义Object来完成扩展,而现在定义泛型。
3、泛型类定义的泛型,在整个类中都有效,如果被方法调用,那么泛型类的对象要明确需要操作的具体类型后,所有要操作的类就已经固定了。
4、类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的。
二、语法格式:
1、定义
public class GenerDao1<T>{
private T field;
public void save(T obj){}
public T getByteId(int Id){}
}
2、举例:
扩展:Dao:Data Access Object--->数据访问对象。
DAO通常位于数据持久层,对数据库中的数据进行操作。
对其操作:crud即增上删改查
c:creat,创建、增加; r:read,读取、查询;
u:update,更新、修改 d:delete,删除。
对javaEE的理解:13种技术。简单说就是对数据库的增删改查。
写Dao类有五个基本方法:增删改查,其中查包含查单个和对同类型集合的查询,如同性别或同地区的集合获取。
package cn.itcast.text2;
import java.util.*;
public class GenerticDao<T> {
public static <E> void staMethod(E e){}
public void add(T obj){}
public boolean delete(T obj){
return true;
}
public boolean delete(int id){
return true;
}
public T update(T obj){
return null;
}
public T findByUserName(String name){
return null;
}
public Set<T> findByPlace(String place){
Set<T> set = new TreeSet<T>();
//....
return set;
}
}
三、注意:
1、在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
2、当一个变量被声明为参数时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用,因为静态成员是被所有参数化的类共享的,所以静态成员不应该有类级别的类型参数。
总结:
对泛型的定义:
第一、定义泛型:当又不确定的类型需要传入到集合中,需要定义泛型
第二、定义泛型类:如果类型确定后,所操作的方法都是属于此类型,则定义泛型类
第三、定义泛型方法:如果定义的方法确定了,里面所操作的类型不确定,则定义泛型方法
示例:
class GenerticTest {
public static void main(String[] args) {
//创建泛型类对象
GenClass<Worker> g = new GenClass<Worker> ();
g.setTT(new Worker());
Worker w = g.getTT();
g.showC(w);
System.out.println("----------------------");
//泛型方法测试
GenMethod<String> g1 = new GenMethod<String>();
GenMethod.showS("SSS");
g1.show("sesf");
g1.print("heheh");
g1.printY(new Integer(5));
System.out.println("------------------------");
//泛型接口测试
GenInter g2 = new GenInter();
g2.show("haha");
System.out.println("Hello World!");
GenImpl<Integer> g3 = new GenImpl<Integer>();
g3.show(new Integer(95));
}
}
//泛型类
class GenClass<TT> {
//定义私有属性
private TT t;
//定义公共设置方法,设置属性
public void setTT(TT t) {
this.t = t;
}
//定义公共访问方法,访问属性
public TT getTT() {
return t;
}
//定义方法
public void showC(TT t) {
System.out.println("GenClass show:" + t);
}
}
//创建Worker类,作为类型传入泛型类中
class Worker {}
//泛型方法
class GenMethod<T> {
//静态的泛型方法
public static <S> void showS(S s) {
System.out.println("static show:" + s);
}
//非静态泛型方法
public void show(T t) {
System.out.println("未指定T show:" + t);
}
public void print(T t) {
System.out.println("指定T print:" + t);
}
//指定接受其他类型的泛型方法
public <Y> void printY(Y y) {
System.out.println("和类指定的不同,为Y print:" + y);
}
}
//泛型接口
interface Inter<T> {
void show(T t);
}
//一般类实现泛型接口
class GenInter implements Inter<String> {
public void show(String s) {
System.out.println("接口 show:" + s);
}
}
//泛型类实现泛型接口
class GenImpl<T> implements Inter<T> {
public void show(T t) {
System.out.println("类接收类型不确定的实现接口 show:" + t);
}
四、泛型集合类
能写出下面的代码即代表掌握了Java的泛型集合类:
HashMap<String,Integer> hm = new HashMap<String,Integer>();
hm.put("zxx",19);
hm.put("lis",18);
Set<Map.Entry<String,Integer>> mes= hm.entrySet();
for(Map.Entry<String,Integer> me : mes) {
System.out.println(me.getKey() + ":" + me.getValue());
}
对在jsp页面中也经常要对Set或Map集合进行迭代:
<c:forEach items=“${map}” var=“entry”>
${entry.key}:${entry.value}
</c:forEach>
五 类型参数的类型推断
一、概述:
1、定义:编译器判断泛型方法的实际参数的过程,称之为类型推断。
2、类型推断是相对于直觉推断的,其实现方法是一种非常复杂的过程。
二、类型推断的具体规则:
根据调用泛型方法时,实际传递的参数类型或返回值的类型来推断。
具体规则如下:
1.当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) --> static <E> void swap(E[] a, int i, int j)
2.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) --> static <T> T add(T a, T b)
3.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) --> static <T> void fill(T[] a, T v)
4.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型, 并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f) --> static <T> T add(T a, T b)
5.参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5])--> static <T> void copy(T[] a,T[] b);
copy(new Vector<String>(), new Integer[5]) --> static <T> void copy(Collection<T> a , T[] b);
六 定义泛型类型
1.如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:
public class GenericDao<T> {
private T field1;
public void save(T obj){}
public T getById(int id){}
}
2.类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:
GenericDao<String> dao = null;
new genericDao<String>();
3.注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
问题:类中只有一个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛型?
答:方法级别的泛型
4.泛型的继承
泛型的继承有两种方式:
1)继承时,对泛型父类的类型参数不实例化
2)继承时,对泛型父类的类型参数实例化
七 扩展--> 通过反射获得泛型的参数化类型
示例1:
package cn.itcast.text2;
import java.lang.reflect.*;
import java.sql.Date;
import java.util.*;
import cn.itcast.text1.ReflectPoint;
public class GenerticTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Object obj = "abc";
String str = autoContor(obj);
GenerticDao<ReflectPoint> gd = new GenerticDao<ReflectPoint>();
gd.add(new ReflectPoint(3,5));
//通过获得方法本身的方法
Method applyMethod = GenerticTest.class.getMethod("applyVector", Vector.class);
//通过方法的获取泛型参数的方法得到原始参数类型的集合
Type[] types = applyMethod.getGenericParameterTypes();
//将参数类型转换为参数化类型
ParameterizedType pType = (ParameterizedType)types[0];
//得到原始类型
System.out.println(pType.getRawType());
//得到实际参数类型
System.out.println(pType.getActualTypeArguments()[0]);
}
示例2:
示例代码:
Class GenericalReflection {
private Vector<Date> dates = new Vector<Date>();
public void setDates(Vector<Date> dates) {
this.dates = dates;
}
public static void main(String[] args) {
Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric", Vector.class);
ParameterizedType pType = (ParameterizedType)
(methodApply .getGenericParameterTypes())[0];
System.out.println("setDates("
+ ((Class) pType.getRawType()).getName() + "<"
+ ((Class) (pType.getActualTypeArguments()[0])).getName()
+ ">)" );
}
}
泛型DAO的应用:
public abstract class DaoBaseImpl<T> implements DaoBase<T> {
protected Class<T> clazz;
public DaoBaseImpl() {
Type type = this.getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) type;
this.clazz = (Class) pt.getActualTypeArguments()[0];
System.out.println("clazz = " + this.clazz);
}
}
public class ArticleDaoImpl extends DaoBaseImpl<Article> implements ArticleDao {
}