JDK 1.5新特性Java泛型
一. 什么叫泛型?
泛型就是一种类型限定,是一种类型操作安全机制,解决在运行期间发生的ClassCastException异常,将类型异常转换到了编译期间,让程序员在编码过程就很直观的发现问题,解决类型安全问题。
简单的说,泛型就是一种类型预定义,是一种类型操作安全机制。
二. 泛型的特点
1. 泛型是提供给Javac编译器使用的,用来限定参数化的泛型类型以及在集合中存放数据类型的唯一性,在编译期杜绝了数据类型不匹配的问题,当编译通过后,生成的字节码已经去掉了泛型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据,如用发射的方法往集合中添加不同类型的数据。
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());
2. 在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
三. 泛型的作用是什么?
很好的解决了如下问题:
A. 将运行期间的ClassCastException异常转换到了编译期间,让程序员在编码过程就能直接的发现问题,解决类型安全问题。
B. 避免了强制转换。
C. 泛型的参数化,使程序方法,类更具扩展性。
四. 泛型的类型
1. 类泛型
就是将泛型定义在类上,定义在类上的泛型,其作用域在整个类上都有效,方法和属性也可以使用类上的泛型定义。
类泛型的定义
class 类名 <T>
{
…..
}
类泛型的作用域:
泛型定义在类上当一个类中的多数功能可以操作多种类型的数据,但是此时此刻,只用来操作一种类型的数据,那么可以将泛型定义在类上,这样就泛型就可以作用到所有的成员方法上,类泛型作用范围是整个类中。
class ShowMessage<T>//类泛型的定义方式
{
public void show(T t1)
{
System.out.println("show: "+ t1);
}
public void print(T t2)
{
System.out.println("show: "+ t2);
}
}
public class GenericsDemo1
{
public static void main(String[] args)
{
ShowMessage<String> sm = new ShowMessage<String>();
sm.show("abc");
sm.print("1234");
}
}
类泛型的注意事项
A. 静态方法不能使用类上定义的泛型,因为静态属性和方法随着类的加载而加载,而在类上定义的泛型是在创建对象的时候使用,而创建对象之前,静态方法或属性是访问不到泛型所接受的类型的,所以如果静态属性和方法要使用泛型,就必须在其自身定义,不能使用类的泛型。
B. 泛型定义在类上当一个类中的多数功能可以操作多种类型的数据,但是此时此刻,只用来操作一种类型的数据,那么可以将泛型定义在类上,这样就泛型就可以作用到所有的成员方法上,类泛型作用范围是整个类中。
C. 当一个类中的不同方法可能要操作不同的数据的时候,这个时候可以将泛型定义在方法上,这样就每个方法都可以的独立操作自己需要操作的数据类型,当然如果在类上面也定义了泛型,那么类中的方法也可以使用类上的泛型静态方法不能使用类上的泛型,因为静态方法是随着类加载的时候加载,而类上的泛型是在对象创建的时候使用,可是在没创建对象前静态方法就存在了,那么使用类上的泛型就会出错,所以静态方法如果用使用泛型,就只能定义在静态方法上面。
2.方法上的泛型
定义:定义在方法上的泛型,叫做方法泛型
方法泛型的定义:
访问权限修饰符返回值类型方法名(T t1)
{
……..
}
注意:
A. 只有引用类型才能作为泛型方法的实际参数
B. 位置:用于放置泛型的类型参数的<>应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前,按照惯例,类型参数通常用单个大写字母表示。
C. 可以用类型变量表示异常,称之为参数化的异常,可用于方法的throws列表中,但是不能用于catch子句中。
D. 在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开。
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;
}
}
class Robot<E>
{
static int count = 0;
{
count++;
System.out.println("我是机器人多拉A梦"+count+"!");
}
public <T> void showNumber(T t1 , T t2 )
{
System.out.println("第一个参数是:" + t1+"\n第二个参数是: "+t2);
}
public <Q> void sayHello(Q q)
{
System.out.println("Hello "+ q+" !");
}
public void question(E e)
{
System.out.println(e + "? what's this?");
}
//静态方法定义
public static <D> void singing( D d)
{
System.out.println("正在唱《"+d+"》");
}
}
public class GenericsDemo2
{
public static void main(String[] args)
{
Robot r = new Robot();
r.sayHello("Ansen");
r.showNumber(1,2);
r.singing("猪之歌");
Robot<String> r2 = new Robot<String>();
r2.question("java");
r.singing("中华人民共和国国歌");
Robot<Integer> r3 = new Robot<Integer>();
r3.question(631150873);
r.singing("讲不出再见");
}
}
五. 关于参数化泛型使用的几点说明
1. 参数化类型与原始类型的兼容性
A. 参数化类型可引用一个原始类型的对象,编译只是报警告,能不能通过编译,是编译器说了算。
如:Collection<String> coll = new Date();
B. 原始类型可以引用一个参数化类型的对象,编译器报告警告
Collection coll = new Vector<String>();à编译通过
2. 参数化类型不考虑类型参数的继承关系
Vector<String> v = new Vector<Objec>();//错误的
不写Object没错,写了就是明知故犯
3. 在创建数组实例时,数组的元素不能使用参数化的类型
Vector<Integer> v[] = new Vector<Integer>[10];//错误的
import java.util.ArrayList;
public class GenericsDemo3
{
public static void main(String[] args)throws Exception
{
ArrayList<String> as = new ArrayList<String>();
as.add("Ansen");
as.add("1989");
System.out.println(as.get(0));
System.out.println(as.get(1));
ArrayList<Integer> ai = new ArrayList<Integer>();
ai.add(9);
ai.add(11);
System.out.println(ai.get(0));
System.out.println(ai.get(1));
编译器生成的字节码会去掉泛型的类型信息
System.out.println(as.getClass() == ai.getClass());
System.out.println(as.getClass().getName());
System.out.println("=========反射方法================");
//如下编译报错
//as.getClass().getMethod("add",Object.class).invoke(as,2013);
//as.getClass().getMethod("add",Object.class).invoke(as,"yes");
//System.out.println(as.size());
//System.out.println(as.get(2));
//System.out.println(as.get(3));
ai.getClass().getMethod("add",Object.class).invoke(ai,2013);
ai.getClass().getMethod("add",Object.class).invoke(ai,"yes");
System.out.println(ai.size());
System.out.println(ai.get(2));
System.out.println(ai.get(3));
}
}
六. 泛型中的通配符
1. 定义:任意类型包容,当传入的类型不确定的时候,可以使用通配符号“?”
2. 特点:
A. 使用?通配符可以接收任意类型引用,通配符的变量主要作用,也可以调用与参数无关的方法,但不能调用与参数化无关的方 法。
Collection<?>coll=new HashSet<Date>();
Collection<?>coll2 =new ArrayList<Robot>();
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. 定义:对于一个范围内的一类事物,可以通过泛型限定的方式定义.
2. 两种范围限定:
A. ? extends E:可接收E类型或E类型的子类型;称之为上限。
Vector<? extends Number> x = newvector<Integer>();
B. ? super E:可接收E类型或E类型的父类型;称之为下限。
Vector<? super Integer>x = newvector<Number>();
/*
泛型的限定:
*/
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);
}
}
八. 模版函数的引入.
如下函数的结构很相似,仅类型不同:
int add(int x,int y)
{
return x+y;
}
float add(float x,float y)
{
return x+y;
}
double add(double x,double y)
{
return x+y;
}
C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,示意代码如下:
template<class T>
T add(T x,T y)
{
return (T) (x+y);
}
九. 泛型类型的推断
1. 编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
2. 根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
A. 当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) --> static <E> void swap(E[] a, int i, int j)
B. 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) --> static <T> T add(T a, T b)
C. 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) --> static <T> void fill(T[] a, T v)
D. 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f) --> static <T> T add(T a, T b)
E. 参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为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.反射获得泛型的实际类型参数
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 {
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]);
}
1. 数据的增删改查
扩展: Dao(Data Access Object)数据访问对象。
对其操作: 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;
}
}