第15章 泛型(下)

15.10.3无界通配符
1、无界通配符?意味着“任何事物”。
2、List实际上也是List 。List实际上表示“持有任何Object类型的原生List”。
而Lis<>?表示“具有某种特定类型的非原生List,只是不知道那种类型是什么”
15.10.4 捕获转换
1、有一种情况特别需要使用<>?而不是原生类型。如果向一个使用<>?的方法传递原生类型,那么对编译器来说,可能会推断出实际的类型参数,使得这个方法可以回转并调用另一个使用这个确切类型的方法。称为“捕获转换”。
2、捕获转换的工作情况:即在方法内部,需要使用确切的类型。
15.11 问题
15.11.1 任何基本类型都不能作为类型参数

public class ByteSet {
    Byte[] bytes = {1,2,3,4,5,6,7,8,9};
    Set<Byte> set = new HashSet<>(Arrays.asList(bytes));
    //Set<Byte> set1= new HashSet<>(Arrays.<Byte>asList(1,2,3,4,5,6,7,8,9));  not ok  byte not int
    Set<Byte> set2= new HashSet<>(Arrays.<Byte>asList(bytes));
}

15.11.2 实现 参数化接口
1、一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。
15.11.3转型和警告
1、使用带有泛型类型参数的转型或instaceof不会有任何效果。

class Widget{}
public class ClassCasting {
    public void f(String[] args) throws Exception{
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(args[0]));
        //不能这样声明
       // List<Widget> lw=List<Widget>.class.cast(objectInputStream.readObject()); //not ok
        List<Widget> lw1=List.class.cast(objectInputStream.readObject()); //ok
        List<Widget> lw2=(List<Widget>)List.class.cast(objectInputStream.readObject()); //warning
    }
}

15.11.5基类劫持了接口
1、

package com15下;

/**
 * Created by Panda on 2018/5/16.
 */
public class ComparablePet implements Comparable<ComparablePet> {
    @Override
    public int compareTo(ComparablePet o) {
        return 0;
    }
}
 //not ok  once Comparable 确定了ComparablePet 参数 其他任何实现类都不能与ComparablePet之外的任何对象进行比较
/*class Cat extends ComparablePet implements Comparable<Cat>{
    @Override
    public int compareTo(Cat o) {
        return 0;
    }
}*/
// it is ok
class Hamster extends ComparablePet implements Comparable<ComparablePet>{
    public int compareTo(ComparablePet arg){
        return 0;
    }
 }

15.12 自限定的类型
1、在Java泛型中,有一个经常性出现的惯用法:
class SelfBounded

class B<T>{}
public class A extends B<A>{
}

1、古怪的循环泛型(CRG)。古怪的循环是指类相当古怪地出现在它自己基类中这个事实。
2、创建一个类,它继承自一个泛型类型,这个泛型类型接受类的名字作为其参数。
3、CRG的本质:基类用导出类替代其参数。意味着泛型基类变成了一种其所有导出类的公共功能的模板,但是这些功能对于其所有参数和返回值,将使用导出类型。也就是说,在产生的类中将使用确切类型而不是基类型。

class BasicHolder<T>{
    T elememt;
    void set(T arg){elememt=arg;}
    T get(){return elememt;}
    void f(){
        System.out.println(elememt.getClass().getSimpleName());
    }
}
class SubType extends BasicHolder<SubType>{}
public class Test {
    public static void main(String[] args) {
        //新类Subtype接收的参数和返回的值具有SubType类型而不仅仅是基类BasicHolder类型。
        SubType subType = new SubType();
        SubType subType1 = new SubType();
        subType.set(subType1);
        SubType subType2 = subType.get();
        subType.f();
    }
}

15.12.2自限定
1、自限定将采取额外的步骤,强制泛型当作其自己的边界参数来使用。
2、自限定所做的就是要求在继承关系中,如下面的描述
Class A extends SelfBounded{} 会强制要求将正在定义的类当作参数传递给基类。
15.12.3参数协变
1、自定义类型的价值在于它们可以产生协变参数类型—方法参数类型会随子类而变化。尽管自限定类型还可以产生于子类型相同的返回类型。

package com15下;

/**
 * Created by Panda on 2018/5/16.
 */
class Base{}
class Derived{}
class GenericSetter<T>{
    void set(T arg){
        System.out.println("GenericSetter.set(Base)");
    }
}
class Derivedgs extends GenericSetter<Base>{
    void set(Derived derived){
        System.out.println(" Derivedgs.set( Derived)");
    }
}
public class Test1 {
    public static void main(String[] args) {
        Base base = new Base();
        Derived derived = new Derived();
        Derivedgs derivedgs = new Derivedgs();
        derivedgs.set(derived);
        derivedgs.set(base);
    }
    /**
     * Derivedgs.set( Derived)
     GenericSetter.set(Base)
     */
}

15.13 动态类型安全
1、java.util.Collection()中有一组便利工具,可以解决类型检查问题,它们是静态方法:checkedCollection() checkedList() checkedMap() checkSet() checkedSortedMap()和checkedSortedSet()。这些方法每一个都会将你希望动态检查的容器当作第一个参数接受,并将希望强制要求的类型作为第二个参数接受。
15.14异常
1、由于擦除的原因,将泛型应用于异常是非常受限的。catch语句不能捕获泛型类型的异常,因为在编译期和运行时都必须知道异常的确切类型。
2、泛型类也不能直接或间接继承自Throwable
15.15 混型
1、混型:混合多个类的能力,以产生一个可以表示混型中所有类型的类。
2、混型的价值之一是它们可以将特性和行为一致地应用于多个类之上。如果想在混型类中修改某些东西,作为一种意外的好处,这些修改将会应用于混型所应用的所有类型之上。正犹豫此,混型有一点面向方面编程(AOP)的味道,而方面经常被建议用来解决混型问题。
15.15.1C++中的混型
15.15.2与接口混合
15.15.3使用装饰者模式
1、装饰器模式使用分层对象来动态透明地向单个对象中添加责任。装饰器指定包装在最初的对象周围的所有对象都具有相同的基本接口。某些事物是可装饰的,可以通过将其他类包装在这个可装饰对象的周围,来将功能分层。这使得对装饰器的使用是透明的—无论对象是否被装饰,都拥有一个可以向对象发送的公共消息集。装饰类可以添加新方法,但是受限的。
2、装饰器是通过使用组合和形式化结构(可装饰物/装饰器层次结构)来实现的,而混型是基于继承的,。因此可以将基于参数化类型的混型当作一种泛型装饰器机制,这种机制不需要装饰器设计模式的继承结构。
3、

package com15下;


import java.util.Date;

/**
 * Created by Panda on 2018/5/16.
 */
class Basic{
    private String value;
    public void set(String val){value=val;}
    public String get(){return value;}
}
class Decorator extends Basic{
    public Decorator() {
    }
    protected Basic basic;
    public Decorator(Basic basic){this.basic=basic;}
    public void set(String val){basic.set(val);}
    public String get(){return basic.get();}
}
class TimeStamped extends Decorator{
    private  long timeStamp;
    public TimeStamped(Basic basic){
        super(basic);
        timeStamp=new Date().getTime();
    }
    public long getTimeStamp(){return timeStamp;}
}
class SerialNumbered  extends Decorator{
    private  static long count=1;
    private final long serialNumber=count++;
    public SerialNumbered(Basic basic){
        super(basic);
    }
    public long getSerialNumber(){return  serialNumber;}
}
public class Decoration {
    //尽管可以添加多个层,但是最后一层才是实际类型。因此只有最后一层的方法是可视的。
    //而混型的类型是所有被混合到一起的类型。因此对于装时期来说,其明显的缺陷是它只能
    //有效的工作于装饰中的一层(最后一层)
    public static void main(String[] args) {
        TimeStamped timeStamped = new TimeStamped(new Basic());
        TimeStamped timeStamped1=new TimeStamped(new SerialNumbered(new Basic()));
      //  timeStamped1.getSerialNumber(); //not ok
        timeStamped1.getTimeStamp(); // ok
        SerialNumbered serialNumbered = new SerialNumbered(new Basic());
        SerialNumbered serialNumbered1=new SerialNumbered(new TimeStamped(new Basic()));
        serialNumbered1.getSerialNumber();  //ok
      //  serialNumbered1.getTimeStamp();  //not ok

    }
}

15.15.4 与动态代理结合
1、可以使用动态代理来创建一种比装饰器更贴近混型模型的机制,通过使用动态代理,所产生的类的动态类型将会是已经混入的组合类型。
2、由于动态代理的限制,每个被混入的类都必须是某个接口的实现。
3、只有动态类型而不是非静态类型才包含所有的混入类型。
15.16潜在类型机制
1、潜在类型机制是一种代码组织和复用机制。
15.17对缺乏潜在类型机制的补偿
1、Java不支持潜在类型机制,但是并不意味着有界泛型代码不能再不同的类型层次结构之间应用。也就是说,仍旧可以创建真正的泛型代码。
15.17.1反射
1、

package com15下;

import java.lang.reflect.Method;

/**
 * Created by Panda on 2018/5/16.
 */
class Mime{
    public void walkAgainstTheWind(){}
    public void sit(){
        System.out.println("Pretending to sit");
    }
    public void pushInvisibleWalls(){}
    public String toString(){return "Mime";}
}
class SmartDog{
    public void speak(){
        System.out.println("woof");
    }
    public void sit(){
        System.out.println("sitting");
    }
    public void reproduce(){}
}
class ComunicateReflectively{
    public static void perform(Object speaker){
        Class<?> spkr=speaker.getClass();
        try {
           try {
               Method speak=spkr.getMethod("speak");
               speak.invoke(speaker);
            }catch (NoSuchMethodException e){
               System.out.println(speaker+"cannot speak");
           }
           try{
                Method sit =spkr.getMethod("sit");
                sit.invoke(speaker);
           }catch (NoSuchMethodException e){
               System.out.println(speaker+"cannot sit");
           }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class LatentReflection {
    public static void main(String[] args) {
        ComunicateReflectively.perform(new SmartDog());
        ComunicateReflectively.perform(new Mime());

    }
    /**
     * woof
     sitting
     Mimecannot speak
     Pretending to sit
     */
}

15.7.2 将一个方法应用于序列
1、反射提供了有趣的可行性,但是它将所有的类型检查都转移到了运行时,因此在许多情况下并不是所希望的。如果能够实现编译期类型检查,通常会更符合要求。
15.17.3当并未碰巧拥有正确的接口时
15.17.4用适配器仿真潜在类型机制
1、潜在类型机制:意味着可以编写代码声明。实际上,潜在类型机制创建了一个包含所需办法的隐式接口。
2、从所拥有的接口中编写代码来产生需要的接口,这就是适配器设计模式的一个典型示例。可以使用适配器来适配已有的接口,以产生想要的接口。
15.18将函数对象用作策略
1、策略设计模式,这个设计模式可以产生更优雅的代码,因为它将“变化的事物”完全隔离到了一个函数对象中。函数对象就是在某种程度上行为像函数的对象—一般地,会有一个相关的方法。函数对象的价值就在于,与普通方法不同时,可以传递出去,并且还可以拥有在多个调用之间持久化的状态。
15.19 总结
1、泛型:是一种方法,通过它可以编写出更“泛化”的代码。这些代码对于他们能够作用的类型具有更少的限制,因此单个的代码段可以应用到更多的类型上。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页