Java泛型

Thinking in Java


元组

1、元组:将一组对象直接打包储存于其中的一个单一对象,该容器对象允许读取其中的元素,但是不允许向其中存放新对象;(即 数据传输对象,信使);
2、在Java中可以通过 泛型实现元组对象;
3、Java泛型有一个局限性: 基本类型无法作为类型参数,但是Java SE5具备自动打包和自动拆包功能;



泛型方法

1、使用泛型的基本原则:在任何情况下, 尽量使用泛型方法,如果可以使用泛型方法取代整个泛型类,那就应该使用泛型方法;
2、可变参数和泛型方法
可变参数和泛型方法可以很好地共存:
public static <T>  List<T> makeList(T...args){
     List<T> result = new ArrayList<T>();
     for(T item : args)
          result.add(item);
     return result;
}




匿名内部类

泛型可以应用匿名内部类:
interface Generator<T>{
     public T next();
}
class Customer{
     ......//内部类
     public static Generator<Customer> getGenerator(){
          return new Generator<Customer>(){
               public Customer next(){     return new Customer();}
          }
     }
}
class Teller{
     ......//内部类     
     public static Generator<Teller> getGenerator(){
          return new Generator<Teller>(){
               public Teller next(){     return new Teller();}
          }
     }
}





类型擦除

1、 在泛型代码内部,无法获取任何有关泛型参数类型的信息;
2、 Java泛型时使用擦除实现的,当使用泛型代码时, 任何具体的类型信息都会被擦除;而你唯一知道的是你在使用一个对象,List<String>和List<Integer>在运行的事实上都是相同的类型(这两种类型都会被擦除为它们的原生类型,即 List 或 List<Object>);

3、Java对类型擦除的补偿

(1)泛型对象T无法创建类型实例
     在Java中,创建一个new T()是无法实现的,解决方案是传递一个工厂对象,并使用它来创建新的实例;其中最便利的工厂对象时Class类,使用()方法来创建该类型的新对象;
Class<T> type = T.class;
T demo = type.newInstance();

※这种方法有缺陷:T类型可能没有默认的构造方法,如Integer,此时建议使用显性工厂对其进行限制;
interface Factory<T>{     //工厂类
     T create();
}
class Product<T>{          //产品类,转发泛型类型的载体,实现泛型T的实例化;
     private T x;
     public <F extends Factory<T>> Product(F factory){
          x = factory.create();
     }
}
//具体工厂类,回避Integer没有默认的构造方法,而带来的编译期无法捕获的异常;
class IntegerFactory implements Factory<Integer>{
     public Integer create(){     return new Integer(0);     }
}
//对某个类型使用其内部工厂类
class Widget{
     public static class WidgetFactory implements Factory<Widget>{
          public Widget create(){     return new Widget();     }
     }
}
main(){
     new Productor<Inetegr>(new IntegerFactory());
     new Prodoctor<Widget>(new Widget.WidgetFactory());
}



(2)无法创建泛型数组
①在任何想要创建泛型数组的地方使用 ArrayList<T>;
List<T> list = new ArrayList<T>();
②在集合内部使用 Object[],然后使用数组元素时,添加一个 T的转型;(一般是在访问数据时进行转型)
class GenericArray{
     private Object[] array;
     public GenericArray(int size){
          array = new Object[size];
     }
     public void put(int index,T item){
          array[index] = item;
     }
     @SuppressWarnings("unchecked")        //抑制编译时类型擦除引起的unchecked检查警告;
     public T get(int index){
          return (T)array[index];
     }
     @SuppressWarnings("unchecked")
     public T[] rep(){
          return (T[])array;
     }
}





15.5 通配符

1、List<Number> 不是List<Integer>的父类 ,List<? extends Number>才是 List<Integer>的父类;
2、无界通配符<?>
(1) List 等同于 List<Object>:持有任何Object类型的原生List;
(2) List<?>:具有某种特定类型的非原生List(只是暂时不清楚该类型具体时什么);)
3、List(同List<Object>) 、List<?>、List<? extends Object>声明的对象可以自由相互传递参数,但是:
void method(List<?> list1){
     List<? extends Object>  list = list1;   //会抛出unchecked警告,必要时使用@SuppressWarning("unchecked")阻止;
}

4、转型捕获:
在一种情况下需要使用<?>,如果向一个使用<?>的方法传递一原生类型,编译器可能会推断出实际的参数类型,使该方法可以回转并调用另一个使用这个确切类型的方法;
// 未指定的通配符类型被捕获,并转化为确切的类型;
class Holder<T>{
     private T value;
     public Holder(){     }
     public Holder(T value){     this.value = value;}
     public void set(T value){     this.value = value;}
     public T get(){     return value;}
}
public CaptureConversion{
//接收无界参数为类型,转化为确切类型参数
     static <T> void incept(Holder<T> holder){
          T t = holder.get();
          System.out.println(t.getClass().getSimpleName());   //打印接收类泛型参数类型
     }
//转发无界类型参数
     static void transmit(Holder<?> hodler){
          incept(holder);
     }
//测试,移除警告阻止后,以下警告才会报出;
    @SuppressWarning("uncheked");
     public static void main(){
          Holder h1 = new Holder<Integer>(1);
          transmit(h1);     //no warning,h1成功被转化为Holder<Integer>类型;
         //incpet(h1);    warning;
          Holder h2 = new Holder();
          transmit(h2);     //no warning,h2被转化为Holder<Object>类型;
          Holder<?> h3 = new Holder<Double>(1.0);
          transmit(h3);
     }
}





15.6 自限定的类型

class SelfBounded<T extends SelfBounded<T>>{     ..... }
古怪的循环(CRG):类古怪地出现在自己的基类中;
1、CRG:基类用导出类代替其参数,者意味着 泛型基类变成一种导出类的公共功能的模板,但是这些导 出类对于所有的参数和返回值,将使用导出类型
public class BasicHolder<T>{
     T element;
     void set(T args){     element = args;     }
     T get(){     return elements;     }
     void f (){     System.out.printn(element.getClass().getSimpleName());     }
}
class Subtype extends BasicHolder<Subtype>{ }
main(){
     Subtype st1 = new Subtype();
     Subtype st2  = new Subtype();
     st1.set(st2);
     Subtype st3 = st1.get();
}


//相当于Subtype把BasicHolder当成一种公共模版、类似于C++中的模版类;
2、自限定通过额外的步骤,强制泛型当做自己的边界参数使用;
3、自限定类型的价值在于: 它们可以产生协变参数类型——方法参数类型会随子类而变化;




15.7 动态类型安全

1、因为可以向Java SE5之前的代码传递泛型容器,所以旧代码可能会破坏该容器,在Java SE5的 java
.util.Collections 中有一组便利工具,可检查这种情况下的类型检查问题;
2、这些便利工具为: 静态方法checkedCollection()、checkedList()、checkedMap()、checkedSet()、checkedSortedMap()、checkedSortedSet();

Collection<E> checkedCollection(Collection<E> c, Class<T> type);
//返回一个 c 对象 的动态安全视图,该动态视图只允许type类型的数据可以加入到容器中,否则会抛出ClassCastException异常;
如:
List<Integer> srcList = new List<Integer>();
List<Integer> list = Colleactions.checkedList(srcList, Integer.class);
list.add(new Double(2.0));     //throws ClassCastException;
3、动态安全视图主要与调试,在Java容器类的泛型实现中,泛型类型是以Object存储在容器中,只有在取出时才会被转型为T,可能在此时才会报错, 使用动态视图可以在加入步骤报告错误类型;




15.8 泛型异常
1、由于擦除的原因,泛型应用于异常很受限, catch语句不能捕获泛型类型的异常,因为在编译期和运行期都必须知道异常的确切类型;
2、异常类不能直接或间接继承自Throwable;
3、实现泛型异常的方式:
//①定义抛出泛型异常的过程接口,定义过程抛出泛型异常;
interface Processer<T , E extends Exception>{
     void process(T ) throws E;
}
//在某个具体参数过程中,根据需求抛出具体类型异常;
class Faiure1 extends Exception() {     }
class Processer1 implements Processer<String ,Failure1>{
     public void process(String str) throws Failure1{
          if(str.length()<20)
               throw new Failure("The length of the String is too short");
     }
}
class Failure2 extends Exception() {     }
public Processer2 implements<Integer,Failure2>{
     public void process(Integer num) thorws Failure2{   if() thorw new Failure2();}
}
//测试
main(){
     Process1 p1 = new Process1();
     Process2 p2 = new Process2();
     try{
          p1.process("hello");
          p2.process(233);
     }
     catch(Failure1 f1){     f1.printStackTrace();     }
     catch(Failure2 f2){     f2.printStackTrace();     }
}


15.9 Java泛型使用中的一些问题
1、任何基本类型都不能作为类型参数;
2、实现参数化接口:一个类不能同时实现同一种泛型接口的2种变体,由于擦除的原因,这两个变体会成为一样的接口;
interface Pet<T>{     }
class animal implements Pet<Cat>,Pet<Dog>{     }     //error:
3、重载:
由于擦除的原因,重载方法会产生相同的类型签名,无法通过编译;
public class Demo<W,T>{
     void f(List<T> v){     }
     void f(List<W> v){     }
}//无法通过编译






15.10 混型

1、混型:混合多个类的能力,以产生一个可以表示混型中所有类型的类,它使组装多个类变得简单易行;
2、混型的价值之一是它们可以将特性和行为一致地应用在多个类之上,如果想在混型类中修改某些东西,这些修改将会应用在混型所应用的所有类上;
3、常用的Java混型方式:
(1)与接口混型:某个类通过实现多个定义不同的接口实现混型;
(2)使用装饰器模式:装饰器模式使用对象来动态透明地向当个当个对象中添加责任;
class Basic{
     private String value;
     public void set(String value){     this.value = value;     }
     public String get(){     return value;     }
}
class Decorator extends Basic{
     private Basic basic;
     public Decorator(Basic basic){ this.basic = basic;     }
     public void set(String val){     basic.set(val);     }
     public String get(){     basic.get();}
}
class TimeStamped extends Decorator{
     private final long timeStamp;
     public TimeStamped(Basic baisc ){
          super(basic);
          timeStamp = new Date();
     }
     public long getStamp(){     return timeStamp; }
}


(3)与动态代理混合:可以使用动态代理创建一种比装饰器更加贴近装饰器模型的机制,通过动态代理,所产生类的动态类型将会是已经混入的组合类型;




15.10 潜在类型机制

1、潜在类型机制是一种代码组织和复用机制:编写一次,多次使用,并在一个位置保存代码;(由此可以不必明确地命名代码上的确切接口,以节省代码量);
2、两种支持潜在类型机制的语言为:Python和C++;
3、Java对潜在类型机制的补偿
使用反射:
public class Mime {
       public void sit(){     System.out.println("Pretending to sit");}
       public String toString(){     return "mime";}
       public void pushInvisibleWall(){      }
}
public class Robot {
       public void speak(){     System.out.println("BalBalaBala");     }
       public void work(){      }
       public String toString(){    return "robot";}
}
public class CommunicateReflectively {
       public static void perform(Object speaker){
             Class<?> spkr = speaker.getClass();
             try{
                    try{
                          Method speak = spkr.getMethod("speak");
                          speak.invoke(speaker);
                    }catch(NoSuchMethodException ex){
                          System.out.println(speaker+" cannot not speak!");
                    }
                    try{
                          Method sit = spkr.getMethod("sit");
                          sit.invoke(speaker);      
                    }catch(NoSuchMethodException ex){
                          System.out.println(speaker+" cannot not sit!");
                    }
             }catch(Exception ex){
                    throw new RuntimeException(speaker.toString(),ex);
             }
       }
       public static void main(String[] args){
             perform(new Robot());
             perform(new Mime());      
       }
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值