【java 2】java泛型
来源:互联网 发布:石家庄seo外包 编辑:程序博客网 时间:2024/05/02 15:32
1.Java泛型的由来
2.泛型的作用
List<Apple> box= ...;
Apple apple =box.get(0);//--返回apple实例,不需要类型转换
List box = ...;
Apple apple =(Apple)box.get(0);//--需要类型转换
3.泛型的实现方法
3.1泛型类
具有一个或多个泛型的类。实例:
package mypackage;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;class Point<T>//泛型类{private T m_var;public T GetValue(){return m_var;}public void SetValue(T var){m_var = var;}}public class GenericDemo1 {public static void main(String args[]) throws IOException{//实例化泛型类,泛型指定为StringPoint<String> p=new Point<String>();//提示输入一个字符串System.out.println("Please input a string:");BufferedReader br=new BufferedReader(new InputStreamReader(System.in));String var = br.readLine();p.SetValue(var);System.out.println("Your input is:"+p.GetValue());}}
3.2泛型方法
实例:
package mypackage1;class Demo{//泛型参数列表置于返回值之前public <T> T fun(T var){return var;}}public class GenericDemo2 {public static void main(String args[]){//使用泛型类时,必须在创建时指定类型参数的值;使用泛型方法时,编译器会为//我们找出具体的类型--类型参数诊断Demo d = new Demo();String str = d.fun("strTest");int i = d.fun(100);System.out.println("String output:"+str);System.out.println("Integer output:"+i);}}
对于static方法而言,无法访问泛型类的类型参数,就需要将该方法泛型化。
二、Java泛型的关键技术
1.擦除
通用理解:
擦除是将泛型类型以其父类代替,如String 变成了Object等。其实在使用的时候还是进行带强制类型的转化,只不过这是比较安全的转换,因为在编译阶段已经确保了数据的一致性。
类型擦除的主要过程如下:
1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
2.移除所有的类型参数。
1.1、对于擦除的补偿:
擦除丢失了在泛型代码中执行某些操作的能力,任何在运行时需要某些确切类型的信息都无法工作。
1.2、创建类型实例
用Class类的newInstance方法创建实例—:
class ClassAsFactory<T>{T x;public ClassAsFactory(Class<T> kind){try{x=kind.newInstance();}catch(Exception e){throw new RuntimeException(e);}}}
1.3、创建泛型数组
public class GenericDemo3<T> {private Object[] array;public GenericDemo3(int size){//创建一个Object数组array = new Object[size];}public void put(int index, T item){array[index] = item;}//该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默。@SuppressWarnings("unchecked") public T get(int index){//返回前强制类型转换return (T)array[index];}@SuppressWarnings("unchecked")public T[] rep(){return (T[])array;}}
2.边界
package mypackage3;import java.awt.Color;//import java.awt.Dimension;interface HasColor{Color getColor();}class Colored<T extends HasColor>{T item;Colored(T item){this.item=item;}Color color(){//调用HasColor接口实现类的getColor()方法return item.getColor();}}class Dimension{public int x,y,z;}class ColoredDimension<T extends Dimension & HasColor>{T item;ColoredDimension(T item){this.item=item;}T getItem(){return item;}Color color(){//调用HasColor接口实现类的getColor()方法return item.getColor();}//获取Dimension类中定义的x,y,z成员变量int getX(){return item.x;}int getY(){return item.y;}int getZ(){return item.z;}}interface Weight{int weight();}class Solid<T extends Dimension & HasColor & Weight>{T item;Solid(T item){this.item=item;}T getItem(){return item;}//调用HasColor接口实现类的getColor()方法Color color(){return item.getColor();}int getX(){return item.x;}int getY(){return item.y;}int getZ(){return item.z;}int weight(){return item.weight();}}class Bounded extends Dimension implements HasColor, Weight{public Color getColor(){return null;}public int weight(){return 0;}}public class GenericDemo4 {public static void main(String[] agrs){Solid<Bounded> solid = new Solid<Bounded>(new Bounded());solid.color();System.out.println(solid.getX());solid.getY();solid.getZ();solid.weight();}}
3.通配符
设计如下的类层次结构:Applea=…;
Fruit f=a;
List<Apple> apples=…;
List<Fruit> fruits=apples;
****?extends通配符
List<Apple>apples = new ArrayList<Apple>();
List<? extends Fruit>fruits =apples;
//fruits.add(new Fruit());
//fruits.add(new Apple());
//fruits.add(new Strawberry());
//fruits.add(new Object()); --无法向fruits中添加任何类型的对象
Fruit f=fruits.get(o);//唯一可以完成的操作是从fruits中取出一个对象,因为此时对象的类型是明确的
****?super---超类型通配符
List<Fruit>fruits = new ArrayList<Fruit>();
List<? super Apple>= fruits;//Fruits指向装有Apple的某种超类的List,不明确是什么超类,但知道Apple和任何Apple的子类都跟它的类型兼容
fruits.add(new Apple());
fruits.add(new FujiApple());//可以添加Apple以及Apple的子类
//fruits.add(new Fruit());
//fruits.add(new Object());//但不能添加Apple的任何超类
Object o=fruits.get(0);//从fruits中取出的对象只能确保是Object类型
总结:如果你想从一个数据类型里获取数据,使用? extends通配符
Producer Extends, Consumer Super
无界通配符<?>
public class CaptureConversion{//f1中类型确定,没有通配符或边界static <T> void f1(ArrayList<T> array){T t = array.get(0);System.out.println(t.getClass().getSimpleName());}//f2中ArrayList参数是无界通配符。因此看起来是未知的static void f2(ArrayList<?> array){f1(array);}}
说明:f2()中调用f1(),而f1()需要一个已知参数,这里发生的是:参数类型在调用f2()的过程中被捕获,因此它可以在对f1()的调用中使用。
package mypackage4;class Info<T>{private T var; //定义泛型 变量public void setVar(T var){this.var = var;}public T getVar(){return this.var;}public String toString(){ //直接打印return this.var.toString();}}public class GenericDemo5 {public static void main(String args[]){Info<String> i= new Info<String>(); //使用String为泛型类型i.setVar("test"); //设置内容fun(i);}public static void fun(Info<?> temp){ //可以接收任意的泛型对象System.out.println("内容"+temp);}}
三、泛型的局限性
任何基本类型都不能作为参数类型
Java自动包装机制——自动实现int到Integer的双向转换
包装器类(wrapper)
//Jdk1.5对于Integer类的声明public final class Integerextends Numberimplements Comparable<Interger>
public static void main(String args[]){Integer i=new Integer(1);//Jdk1.5以下版本Integer j=1;//Jdk1.5int a=j.intValue();//手动拆箱int b=j;//自动拆箱j++;//先进行自动拆箱后进行++System.out.println("a="+a+", b="+b+", j="+j);}
//CompileTimeError
Interface Payable<T>{}
Class Employee implements Payable<Employee>{}//都被擦除为Payable,相当于重复实现同一个接口,发生冲突tu
Class Hourly extends Employee implementsPayable<Hourly>{}
思考题:
1.比较Java泛型和C++模板在内部实现机制上有什么不同。(提示:Java泛型使用擦除机制,C++模板呢?)
(1)对于Java泛型的参数类型,任何基本类型都不能作为参数类型,如可以创建一个ArrayList<Integer>数组,但不能创建ArrayList<int>数组;
(2)Java泛型不能实现同一个泛型接口的两种变体,由于擦除原因,这两个变体会成为相同的接口。在 C++ 模板中,编译器使用提供的类型参数来扩充模板,因此,为 List<A> 生成的 C++ 代码不同于为 List<B> 生成的代码,List<A> 和 List<B> 实际上是两个不同的类。而 Java 中的泛型则以不同的方式实现,编译器仅仅对这些类型参数进行擦除和替换。类型 ArrayList<Integer> 和 ArrayList<String> 的对象共享相同的类,并且只存在一个 ArrayList 类。因此在c++中存在为每个模板的实例化产生不同的类型,这一现象被称为“模板代码膨胀”,而java则不存在这个问题的困扰。java中虚拟机中没有泛型,只有基本类型和类类型,泛型会被擦除,一般会修改为Object,如果有限制,例如 T extends Comparable,则会被修改为Comparable。而在C++中不能对模板参数的类型加以限制,如果程序员用一个不适当的类型实例化一个模板,将会在模板代码中报告一个错误信息。
2.尝试使用通配符,完成向一个具有<? extends Fruit>(或者<? super Apple>)类型参数的数组中添加和取出各种类型实例,试试能否成功?
List<? extends Fruit> 表示 “具有任何从Fruit继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List<Apple> 赋值。
List<? super Apple> 表示“具有任何Apple超类型的列表”,列表的类型至少是一个 Apple 类型,因此可以安全的向其中添加Apple 及其子类型。由于List<? super Apple>中的类型可能是任何Apple的超类型。
代码如下:
package mypackage5;import java.util.List;import java.util.ArrayList;import java.util.Date;class Fruit extends Object{}class Apple extends Fruit{}class Strawberry extends Fruit{}class FujiApple extends Apple{}public class GenericDemo6 {List<Apple> apples = new ArrayList<Apple>();public void upperBound(List<? extends Fruit> fruits){fruits=apples;//无法向fruits中添加任何类型的对象fruits.add(new Fruit());fruits.add(new Apple());fruits.add(new Strawberry());fruits.add(new Object());fruits.add(null);//唯一能add是null//唯一可以完成的操作时从fruit作用取出一个对象,因为此时对象是明确的。Fruit f = fruits.get(3);Apple a = (Apple)fruits.get(2);}List<Fruit> fruits1 = new ArrayList<Fruit>();//Fruits指向装有Apple的某种超类的List,不明确是什么超类,但知道Apple和任何Apple的子类都跟它的类型兼容public void lowerBound(List<? super Apple> fruits2){fruits2=fruits1;//可以添加Apple以及Apple的子类fruits2.add(new Apple());fruits2.add(new FujiApple());//但不能添加Apple的任何超类fruits2.add(new Object());fruits2.add(new Fruit());//从fruits中取出的对象只能确保是Object类型Object o=fruits2.get(1);Fruit f=fruits2.get(1);}}
存在疑问:
为什么如果fruits变量不是方法的参数时,根本无法使用add方法??该问题尚未解决。
3.在Web开发中,我们经常会遇到将对象序列化并传递的问题。利用泛型设计一个工具类,完成将某个对象转换成其他对象类型(如XML)的功能。
将一个java对象转换成xml文件,或者xml文件转换为一个java对象。采用jaxb api就可以做到。由于初步学习,参考了相关资料。
具体实现步骤如下:
1.创建一个GenericTest工程,new一个mypackage6的包,在包里new两个类,Person1.java和GenericDemo6.java。
2.Person1.java类主要是用于测试代码的java对象。
package mypackage6;import java.util.Calendar;import javax.xml.bind.annotation.XmlAttribute;import javax.xml.bind.annotation.XmlElement;import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name="Test",namespace="mypackage6") //这句很重要,否则会报错public class Person1 { @XmlElement Calendar birthDay; //birthday将作为person的子元素 @XmlAttribute String name; //name将作为person的的一个属性 public Address getAddress() { return address; } @XmlElement Address address; //address将作为person的子元素 @XmlElement Gender gender; //gender将作为person的子元素 @XmlElement String job; //job将作为person的子元素 public Person1(){ } public Person1(Calendar birthDay, String name, Address address, Gender gender, String job) { this.birthDay = birthDay; this.name = name; this.address = address; this.gender = gender; this.job = job; } }//Gender枚举enum Gender{ MALE(true), FEMALE (false); private boolean value; Gender(boolean _value){ value = _value; }}//Address类class Address { @XmlAttribute String country; @XmlElement String state; @XmlElement String city; @XmlElement String street; String zipcode; //由于没有添加@XmlElement,所以该元素不会出现在输出的xml中 public Address() { } public Address(String country, String state, String city, String street, String zipcode) { this.country = country; this.state = state; this.city = city; this.street = street; this.zipcode = zipcode; }public String getCountry() { return country; }}
2.GenericDemo6是主要的实现代码和main测试代码。
package mypackage6;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.util.Calendar;import javax.xml.bind.JAXBContext;import javax.xml.bind.JAXBException;import javax.xml.bind.Marshaller;class JaxbUtils<T>{//T为泛型private static final String DEFAULT_ENCODING = "gbk";/** * 指定对象和编码方式将对象解析为xml字符串 * @param <T> * * @param obj * @param encoding * @return * @throws IOException * @throws JAXBException */public String objectToXmlString(T obj, String encoding) throws JAXBException, IOException {return objectToXmlString(obj, true, false, encoding);}/** * 按照默认的编码方式将对象解析为xml字符串 * @param <T> * * @param obj * @return * @throws IOException * @throws JAXBException */public String objectToXmlString(T obj) throws JAXBException, IOException {return objectToXmlString(obj, null);}/** * * @param <T> * @param obj * @param isFormat * 是否格式化 * @param cancelXMLHead * 是否省略xml文件头 * @param encoding * 编码方式, 默认为“gb312” * @return * @throws JAXBException * @throws IOException */public String objectToXmlString(T obj, boolean isFormat,boolean cancelXMLHead, String encoding) throws JAXBException, IOException {if (encoding == null) {encoding = DEFAULT_ENCODING;}JAXBContext context = JAXBContext.newInstance(obj.getClass());//获取对象类Marshaller m = context.createMarshaller();m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, isFormat);m.setProperty(Marshaller.JAXB_FRAGMENT, cancelXMLHead);m.setProperty(Marshaller.JAXB_ENCODING, encoding);ByteArrayOutputStream out = new ByteArrayOutputStream();m.marshal(obj, out);String xmlString = new String(out.toByteArray());//xmlString为最后解析后的xml文件内容out.flush();out.close();System.out.println(xmlString);return xmlString;}}public class GenericDemo6{public static void main(String args[]){JaxbUtils<Person1> var=new JaxbUtils<Person1>();Address address = new Address("China", "Beijing", "Beijing","xituchenglu 10", "100876");Person1 p = new Person1(Calendar.getInstance(), "zhenghaimin", address,Gender.FEMALE, "student");try {var.objectToXmlString(p);} catch (JAXBException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
执行结果如下:
<?xml version="1.0" encoding="gbk" standalone="yes"?><ns2:Test name="zhenghaimin" xmlns:ns2="mypackage6"> <birthDay>2012-11-15T18:17:53.905+08:00</birthDay> <address country="China"> <state>Beijing</state> <city>Beijing</city> <street>xituchenglu 10</street> </address> <gender>FEMALE</gender> <job>student</job></ns2:Test>
PS:好吧,以前没用过Java,发现eclipse改代码实在能力太强了,用它编程有点像用傻瓜相机照相的感觉。。。=。=!
网易博客:http://bingxinye1.blog.163.com/blog
- 【java 2】java泛型
- Java 泛型 Java generic
- Java Tutorials_Generics(java泛型)
- Java基础 Java 泛型
- 【Java】(2)Java反射
- JAVA [ 泛型 --- 2 ]
- java泛型2
- java泛型(2)
- JAVA泛型2
- java泛型2
- java-16(2)-泛型
- java
- JAVA
- JAVA
- JAVA
- java
- Java
- Java
- 基于注解的 Spring MVC 简单入门
- 输出组合
- 关于加入第三方framework编译的出现的问题
- ubuntu 下共享文件夹
- iphone内存检测
- 【java 2】java泛型
- 二分查找C代码
- jquery设置元素的readonly和disabled
- 比较java.io.Externalizable和java.io.Serializable
- 计算两线夹角的实验
- main() 参数
- Java源码编译过程
- SIG Crash Logs in Xcode(转)
- SpringMVC拦截器简单使用