java语法基础 - 第五部分

121 阅读21分钟

文章目录

类型信息Class

只保存定义类、接口、方法时泛型的占位符,而不会保存实例化时的确切类型信息

Class a<T, M, N>{}
String str1 = Arrays.toString( a.class.getTypeParameters() );
String str2 = Arrays.toString( (new a<Integer, String, Float>()).getClass().geetTypeParameters() );
System.out.println( str1 );
System.out.print( str2 );    // l两者输出的信息完全一样,并没有保存已经确切定义时的信息


RTTI(Run-Time Type Information) :在运行时,识别一个对象的确切类型(多态)

  • 类加载器: 加载( 创建Class对象 ) → 链接( 为静态域分配存储空间 ) → 初始化( 初始化静态域 )
  1. Class对象:保存类的信息
  2. 每个类都有Class对象 —— 每当编译一个新类,就会产生Class对象
  3. 产生该Class对象 → 使用JVM的类加载器的子系统
  4. 一旦“对应类”的Class对象载入内存,它就可以创建“对应类”的对象
  5. 静态常量: 编译器常量(确定值得大小),无需对类初始化
Class方法:
  • forName( 完全限定名 ) —— 完全限定名:包名+类名    注:可能会抛出异常,所以需捕捉异常会初始化类
  • 类名.class —— 生成一个该类名的Class对象,无须捕捉异常 ——不会初始化类

同一种类型的对象(类名完全相同,不能继承),他们公用一套Class信息对想

Auto auto1 = new Auto();
Auto auto2 = new Auto();
if( auto1.getClass() == auto2.getClass ) {          // output: equal
    System.out.println("equal");                      //  故可得知同种类的Class对象相同(共用)
 }

 

  • getName() = getCanonicalName(): 返回包名+类名
    getSimpleName(): 返回类名
  • 类型比较对象 instanceof 类名
    Class实例 . isInstance( 对象 )
    Class实例 . isAssignableFrom( Class实例 )
  • getConstructors()、getConstructor(参数类型信息数组) —— 得到的是有public修饰的构造函数
    getDeclaredConstructors()、getDeclaredConstructor(参数类型信息数组) —— 得到的是所有定义构造函数即使是private
  • Class实例.cast( Object a): 返回一个“无检查”的"强制转型" (T)a
  • Class实例.getClasses(): 返回一个该类内定义的公有类信息
  • Array.newInstance(数组类型信息, 维数数组): —— 返回一个数组对象
  • getComponentType() : —— 返回 数组的组件信息的Class 非数组则返回null
  • getEnclosingClass() : —— 返回该内部类的上一层类的信息
class A {
    public static B {
        public class C {
        }
    }
}
// 测试
A.B b = new A.B();
b.getClass().getEnclosingClass()      // 返回  A的class信息
A.B.C c = b.new C();
c.getClass().getEnclosingClass()   // 返回  B的class信息
泛型的Class引用:Class
  • 加入 泛型<T> 仅仅只是为在编译期间进行检查,而不必在运行期自己发现。
  • 可以指定固定类型的Class引用    注意:Class T指定了什么就是什么,即使是父类也不能用其作为引用 —— 除非在 <>显示的写明继承
Class<Integer> integer = int.class
// integer = Number.class    这是错误的,即使Number是Integer的父类,因为class<Integer> 与 class<Number> 无继承关系。

Class<?> random = double.class  // " ? " 代表什么类型都可以
random = integer;

Class<? extends Number> number= int.class;    // 只要继承与Number类的都可以。
number = double.class;

Class<Son> son = Son.class;
// cCass<Father> father = son.getSuperclass();  虽然son的继承父类是Father但也不能明写,只能用下面那种 因为源码该函数返回的是  class<? super T>
Class<? super Son> son =  son.getSuperclass();



强制转换

Class<Father> father = Father.class;
Son son = new son();
Father fa = father.cast(son);     // 强制转换  转换成Class<T> T的类型

Father fa = (Father)son        //前面 三步 等价于 一步
Class得到的反射 - 暴露一切
  • 运用Class类型信息得到的反射 —— 可以使整个类所有暴露出来,即使是私有,依然可以被调用,俗称“ 后门 "
  • 访问private\protected时,注意设置 (Method/Field实例).setAccessible( true ); → 用来抑制java语言访问权限
  • 反射依然不能修改final变量 —— 反射set不会报错
  • javap -p -private class文件:该语句能显示所有在该类明面写的方法
interface Father {
    public void one();
    String str2 = "str2";
}
class Son extends Father {
    private String str1 = "str1";
    public void one() {}
    private void Two() {}
    private final Integer number = 6;   //虽然反射能set final变量并且鬠报错,但终究没有修改成功。
}

public static void main(String[] args) {
    Class classSon  = Son.class; 
    Method[] methods1 = classSon.getMethods();     //返回的是public方法,即使是及成成的也行
    Method[] methods2 = classSon.getDeclaredMethods();   //返回的是在该类"明面"写的所有权限的方法,故不包括没在该类重写的继承方法。
    Field[] fields1 = classSon.getFields();    // 跟上面类似
    Field[] fields2 = classSon.getDeclaredFields()
}
Array —— 可用于生成数组、查值、修改数组值、数组长度
class Test {
    public static void main(String[] args) {
        Integer[] numbers = { 1, 2, 3, 4, 5 };
        int[] numbers2 = {1, 2, 3, 4, 5 };
        
        int length = Array.getLength( numbers );        // 得到数组的长度
        Object integer = Array.get( numbers, 0 );       // 得到非基本类型数组的指定位置元素
        int integer2 = Array.getInt( numbers2, 0 );     // 得到基本类型数组的指定位置元素
        Array.set( numbers, 0, 10);                         //  修改非基本类型数组的指定位置元素
        Array.setInt( numbers2, 0, 10);                 // 修改基本类型数组的指定位置元素
        
        // 创建多维数组
        Integer[][] num1 = Array.newInstance( Integer[].class.getComponentType(), {1,2} );
        Integer[]  num2= Array.newInstance( Integer[].class.getComponentType(), {10} );
        
        // 创建一维数组
        integer[] num3 = Array.newInstance( Integer.class.getComponentType(), 10 );  
        

注册工厂模式 —— (工厂方法变体)
  1. 创建一个泛型为输出对象类的工厂接口,方法为 返回类型为输出对象(即泛型)的方法。
  2. 输出对象类创建一个继承该工厂接口的静态工厂类
Package library;
interface Factory<T> {      // 具体的颜色类内部静态工厂 实现这个接口  这里的T是具体颜色 
    T create();                         // 生成具体颜色。
}

class Color {
    static List< Factory<? extends Color> > colorFactory = new List< Factory<? extends Color> >();        //保存一系列颜色的生成器,即具体颜色工厂器
    static {
        colorFactory.add(new White.Factory());      //初始化“具体颜色工厂器”的列表
    }
}

class White extends Color {
    public static class Factory implements library.Factory<White> {    //实现工厂接口,接口需注意写包名,要不然系统优先用最近的类名。即内部类Factory,以导致出错。
        public White create() {
            return new White();
        }
}
代理模式

厂家是“委托者”,微商代理是“代理者”
“代理者”筛选了客户,且隐藏了“委托类”的实现

静态代理
interface Sell {
    void sell();
}
class Vendor implements Sell {                  //厂家
    public void sell() { System.out.println( "I am Vendor.sell()" ); }
}
class Proxy implements Sell {        //代理者,聚合,且进一步封装接口方法,筛选用户群体
    Sell proxy = new Vendor();
    public void sell() {
        System.out.print("Proxy: ");
        proxy.sell();
}

动态代理 —— 运用上面的接口Sell、委托类Vendor

Proxy.newProxyInstance(代理类的加载器[ClassLoader],需要代理类的接口的Class数组Class[],方法的调用处理器[InvocationHandler] )

 class Handler implements InvocationHandler{ //引用处理器,代替 ”代理者”的筛选作用
    Object proxied = new Vendor();
    public Object invoke(Object Proxy, Method method, Object[] args) {
        System.out.print("Proxied: ");
        return method.invoke(proxied,args);
    }
 }
 
 public static void main(String[] args) {
    ClassLoader cl = Sell.class.getClassLoader():
    Class[] jk = {Sell.class};
    Handler hd = new Handler();
    Sell proxy = (Sell) Proxy.getProxyInstance(cl, jk, hd);    //生成一个动态代理,(类加载器、需要帅选的方法接口,引用处理器)
    proxy.sell();  //只要运行jk参数里面的方法,都会调用引用处理器里面的invoke函数
}
 


方法类Method

  • 获取Method实例

    • Class实例.getMethod(方法名,参数Class数组)    得到指定类中的方法
    • Class实例.getMethods()   得到指定类的所有方法
  • 调用Method实例的方法

    • Method实例.invoke(类对象,实参数组)   
class Test {
    public void cout() { System.out.println("I am cout()"); }
    public static void main(String[] args) throws Exception{ //Class类中的getMethod会抛出异常
        Class test = Test.class;
        Method methodCout = test.getMethod("cout", null);   //获取Method实例——且获取的为Test类中的cout方法
        methodCout.invoke(new Test(), null);     //调用指定类对象中的cout()
}
单例模式

一个类只能构建一个对象的模式

public class Dog {                                  //懒汉模式
    publc static Dog dog = null;          
    private Dog() {
        name = "DogMei";
    }
   public static Dog getDog() {
        if(dog == null) {
            dog = new Dog();
        }
        return dog;
   }
}        
public static void main(String[] args) {    //另一个类的主函数Main
    Dog dog1 = new Dog();                    //错误,不能生成,因为构造函数私有化了
    Dog dog2 = Dog.getDog();                //能运行
}
空对象

更靠近数据,因为对象表示的是问题空间内的实体
空对象逻辑变体: 模拟对象、桩

 
在原类内部定义静态的空对象类

interface Null {}

class Person {
    String name;
    String sex;
    public final static NullPerson NULLPERSON = new NullPerson();     //空对象,到时定义Person对象为空时,直接赋值该静态空对象    Person  people = Person.NULLPERSON
    Person(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
    public String toString() {
        return name+ ", " + sex;
    }
    public static class NullPerson extends Person implements Null {     //空类继承父类实现Null接口,该内部列必须是static,因为空对象要设置静态。需提前加载。
        private NullPerson(){                  //单例,该类只能有一个实例对象
            super("none","none");
        }
    }
}

 
同一父类多种子类定义空对象,用代理设置空对象

interface Null{}
interface Person{                                   //父类
    String getWorkType();
    String getName();
}
class Painter implements Person {                    //可定义多种子类,如果做空对象的话,需每个子类都要写,麻烦,冗余代码。
    String name = "小明";
    public String getName() { return name; }
    public String getWorkType() { return this.class.getSimpleName; }
}

class NullPersonProxyHandler implements InvocationHandler {    //用动态代理进行多种子类空对象生成
    String type;
    Object prooxied = new NullPerson();
    NullPersonProxyHandler(Class<? extends Person> type) {   //Proxy的参数,参数是子类的Class说明
        type = type.getSimpleName() + "NullPerson";
    }
    class NullPerson implements Null,Person {
        public String getName() { return type; }
        public String getWorkType() {return type; }
    }
    public Object invoke(Object proxy, Method method, Object[] args) {
        return method.invoke(np, args);    //覆盖子类的同名方法
    }
}


public static void main(String[] args) {
    Person person = (Person)Proxy.newProxyInstance( Person.class.getClassLoader(), new Class[]{ Person.class, Null.Person } , new NullPersonProxyHandler(Cat.class) );

泛型

泛型细节

适用于许多许多的类型,目的:希望类或方法能够具备最广泛的表达能力 —— 只用一套代码表达更多的类型 —— 泛型参数可以是异常类

在创建参数化类型的实例时,编译器保证负责转型操作,并且保证类型的正确性

初始化时,告诉编译器使用的类型—— 静态检查,单纯是静态检查,依然能突破用其他类型进行调用( 强制转换 )

class Human implements Comparable<Human> {
    String name;
    Human( String name ) { this.name = name; }
    public int compareTo( Human human ) { //只要被调用,直接输出int型结果,不会发生异常
       int thisCode = name.hashCode();
       int humanCode = human.name.hashCode();
       if ( thisCode > humanCode ) return 1;
       else if ( thidCode = humanCode ) return 0;
       else return -1;
    }
}
//测试
Object o = new Object();
Human hum = new Human("lrc");
hum.compareTo( (Individual)o )     // 这里依然能调用成功,而且最后异常  这是不希望发生的,所以依然需要增加类型检测

List list : 能够持有任何Object类型的原生List —— 无编译器期类型检查
List<?> list: 具有某种特定类型的非原生List
List list: 运行前能检测类型的特定类型非原生List

List list1 = new ArrayList<Integer>();
List<?> list2 = new ArrayList<Integer>();
List<Integer> list3 = new ArrayList<Integer>();
list1.add("fadfsdf")  //依然能增加,因为系统直接把形参当作Object类型

失败:list2.add(5)     // 泛型变量,依然没显示的指明 整形5是符合标准的,即使改成 <?extends Integer>也不行,需改成超类型匹配  < ? super Number>,不过这也导致其能加Number字型对象
list3.add(5)    //运行成功

定义泛型边界class A< T entends 类 & 接口 & 接口> —— 只能边界限定一个Class类,但能限定多个interface

  • 在初始化泛型时,只能初始化你完完整整定义的边界的类型 —— 初始化比定义时边界范围不小于
class A{}
interface B{}
interface D{}
class E extends A implements B,D {}
class AGenerator<T extends A & B & D> {}
public static void main(String[] args) {
    AGenerator<E> ag = new AGenerator<E> ();    // 这个是正确的,初始化的泛型参数完完全全符合定义时的边界,范围不小于定义时的范围,
    AGenerator<A> ag = new AGenerator<E> ()     //这个错误的,初始化的边界缩小。

泛型接口 在实现时最好指定 泛型类型 —— 基本类型无法作为类型参数

class Holder<T> {
    private T a;
    Holder(T a) { this.a = a }
}
泛型初始化: Holder<Integer> holder = new Holder<Integer>(5)   // 故T = Integer,编译器进行 T 转化成 Integer

类内部是泛型组合时,当调用泛型组合的方法时,需明确定义边界 —— 只有当你的泛型跨多个类工作时,泛型才显得有所作为

class a {
    public void cout() { System.out.println( " I am a " );
}
class Test<T extends a> {                 // 组合类  对象a是类Test的组合,当调用对象a的成员函数时泛型需要明确泛型的边界
    T a;
    Test(T a) { this.a = a; }
    public void cout() { a.cout() }         //因为定义了<T extends a >这个边界 ,  所以可以调用该方法
}

泛型类型检查(静态检查): 只会在编译期间检查,而在运行期间就不在检查,而选择擦除所有泛型 —— 即最后只是保存为向上转型的Object类型、并且 T 也不会存在

  1. 克服运行时删除T参数类型,只能在初始化时有个字段保存T的类型信息
  2. 然后可以用 Class实例.isInstance( 对象 ) 既可以在 运行时而不仅仅是编译期间判断 输入对象是否符合要求
List<T>  只会在编译期间检查类型,检查完成后擦除泛型变为:   List
public <T> void cout( T a ) {
    if ( a istanceof T ){}   //直接报错,侧面的说明T在运行时已经被完完全全的擦出
}

元组(tuple)

运用泛型将 一组多种不同类型对象 直接存储变为 一个对象(类似组合)—— 数据传送对象,信使


通过继承机制可实现更长的元组

class Tuple<A,B,C> {       // 三元组
    public final A one;
    public final B two;         //定义为public、final这种书写更加的简洁,而不必设置为私有,又要设置get()方法进行获取
    public final C three;
    Tuple(A one, B two, C three) {
        this.one = one;
        this.two = two;
        this.three = three;
    }
}

自定义简易堆栈
class LinkedStack<T> {
    private class Node {        //结点 一个保存内容、一个保存下一个结点的引用
        T item;
        Node next;
        Node() { item = null; next = null;  }
        Node(T item, Node next) { this.item = item;  this.next = next; }
        void isEmpty() { return item == null && next == null; }
    }
    private Node top = new Node();       //末端哨兵,pop先弹出这个结点
    public void push(T item) { top = new Node(item, top); }   //压栈,创建新结点
    public T pop() {                                    //出栈
        T result = null;
        if( top.isEmpty() != true ) {
            result = top.item;
            top = next;
        }
        return result;
    }
    public boolean isEmpty() {  return top.isEmpty() )    //判断该top结点是不是最后一个
}        
适配器模型
  • 一个类的接口变换成客户端所期待的另一种接口 —— 电脑电源插头是三相的,插座(客户端)没有三相的插口,只有两相,这是需要一个转换器三相变两相使得适配插座
  • 在不知类源码的情况下,导致不能重写该类增加功能,只能用现有的类通过继承实现该有的接口
class Fibonaci {                    //假设这个源码的控制权不在你这里-不能重写这个类增加功能 
    int count = 0;
    Integer next() {
       return fib(count++);
    }
    private Integer fib(int a) {
        if( a < 2 ) return 1;
        else return fib(a-2) + fib(a-1);
    }
}

class FibonacciIterator extends Fibonacci implements Iterable {   //适配器,加功能,通过继承实现接口
    int size = 0;
    FIbonacciIterator(int count) { this.size = count; }
    public Iterator iterator<Integer>() {
        return new Iterator<Integer>() {
            int number = size
            public  boolean hasNext() {
                if(number > 0 )
                    reutrn true;
                else return false;
            }
            public Integer next() {
                number--
                return FibonacciIterator.this.next();
            }
      };
}
泛型方法

泛型方法可独立于泛型类,即定义泛型方法可不用定义泛型类

能使用泛型方法取代整个类的泛型化就尽量用泛型方法 —— 可使代码更加的简单清晰

泛型方法对于泛型类的差别是:不必显示声明泛型参数,而通过实参编译器会判断泛型的参数类型

泛型静态方法调用返回值 用来做实参需指明泛型参数类型

泛型参数不能重载相同的泛型行参 —— 运行时,泛型参数类型消失,导致方法签名一样

编译期检查是在数据类型(参数的检查)、而<>泛型里面必须一一对应

下面的成员函数两两互相冲突,导致错误,因为函数调用是在运行时,类型已经抹除,故方法签名都一样重复
public Father<A, B> {
    public void one( List<A> list ) {}
    public void one( List<B> list ) {}
    public void one( List< String > list) {}
    public void one( List< Integer > list) {}
}
class Create {
    public static <T> List list() {
        return new ArrayList<T>();
    }
    public static void test(ArrayList<Integer> a) {}
}

public static void main(String[] args) {
    ArrayList<Integer> listInteger = Create.list()     //这个是可以的,有参数类型判断
    //Create.test( Create.list() )       这个错误,需显示的泛型参数类型T
    Create.test( Create.<Integer>list() )   //这个正确

class GenericMethod {
    public <T> List<T> makeList(T... args) {   
        List<T> list = new ArrayList<T>();
        for( T t : args) { list.add(t); }
        return list;
     }
}

public static void main(String[] args) {
    GenericMethod gm = new GenericMethod();
    System.out.println( gm.makeList(1,2,3,4,5,6)       //调用泛型方法时无需显示声明泛型参数
}    
泛型数组
  • 实质泛型数组最终还只是定义了 Object[],并没有定义指定的泛型数组
  • 创建泛型数组唯一方式: 创建被擦出泛型的数组 → 进行转型
  • 创建数组最好的方式是,构造函数传进去的是初始化后泛型的类型信息

Array.newInstance(类型信息, 长度) —— 这就创建了指定泛型的数组

Class Father<T> {
    T[] arrays;
    Father(int length) {
        arrays = (T[])new Object[length];      // 这里的强制转换一点都没作用,运行时还是赋值了 Object[] 数组
    }
    Father(Class<T> type, int length) {
        arrays = (T[]) Array.newinstance(type, length);     // 这里创建的就是指定初始化后的指定类型数组
    }
    public T[] array() { return arrays; }
}
public static void main(String[] args) {
    Father<Integer>[] fathers =    ( Father<Integer>[] )new Father[]()   //不可缺少转型,否则报错,实质运行时强制转型没什么作用。
    Father<Integer> father = new Father<Integer>();
    Integer[] number  = father.array();          //编译期并无显示错误,运行时直接抛出 “ClassCastException异常”再次说明泛型只是在编译器检测而已,运行期完全不起作用
    Object[] number = father.array();      //这个就正确,因为终究还只是 Object[]
}
边界
  • 子类的泛型约束可直接嫁接到父类 —— 继承
class Father<T> {}
class Son<T extends Number> extends Father<T> {}

 

  • <>内的泛型多继承 可调用其继承类、接口方法
class A { void acout() {} }
interface B { void bcout(); }
class Son<T extends A & B> {
    T item;                         // 此时 item 就有权限调用 类A、接口B 的方法
    public void test() {
        item.acout();
        item.bcout();
}

 

  • 单纯的<> 可加“水果"
class A {}
class B extends A {}
class Test<T> {
    T item;
    Test(T item) { this.item = item; }
    public void set(T item) { this.item = item; }
}

//主函数
public static void main(String[] args) {
    Test<A> test = new Test<A>(new A());
    test.set(new B());    // 并没有错误
}

 

  • 上界通配符<? extends 类> —— 单纯只是为了赋值向上转型,实质并没有像数组一样真正起作用的是指向的引用无协变性 —— 泛型初始化赋值的是?—— 更宽的实参范围
  • <? extends T>作用: 一个能初始化水果的盘子,但不能加水果,因为系统识别不了你是什么水果盘子
class Fruit{}
class Apple extends Fruit;
class Banana extends Fruit;
class Plate<T> {
    T fruit;
    public void set(T fruit) { this.fruit = fruit; }
    public T get() { return fruit; }
}
Plate<? extends Fruit> fruitPlate = new Plate<Apple>();   //系统能识别你是水果盘子,但不能识别你是什么具体类型的水果盘子
fruitPlate.set(new Apple())     // 发生错误,不能识别具体类型T
  • 泛型容器变量 代码意义上的向上转型丢失 添加对象的能力 (Object对象也不行) —— add(T e) 其参数是泛型,故不可以
  • 只要赋值后的泛型变量,调用其泛型变量对象的方法只要形参不是 泛型T 就可以完调用
class Father {}
class Son extends Father {}

Father[] a = new Son[6];
a[0] = new Son();
a[1] = new Father();  //运行错误,在编译前并无错误,因为a变量本来就是Father类型 → 说明数组有协变性


//错误: List<Father> list = new ArrayList<Son>()    泛型的标准是一是一,二就是二,爸爸不认的儿子就不是儿子。
List<? extends Father> list = new ArrayList<Son>()      //泛型变量中爸爸直接认儿子
//错误 ;list.add(new Son() )   一旦泛型变量 “代码意义上的强制转型” 则丢失传递对象的能力,即使Object也不行 —— 形参为泛型

 

  • 超类型通配符(逆变、下界通配符) — < ? super 类/泛型T > —— 泛型初始化赋值的是T —— 传递确定的参数

    1. 使这个泛型能持有某种具体的类型,不再会跟普通的泛型会在运行中系统自动清除
    2. 能放具体水果,且是水果的基类的盘子
引用上面上界通配符的类
Plate<? super Fruit> plate = new Plate<Fruit>();
Plate<? super Fruit> plate = new Plate<Object>();
plate.set(new Apple());      //能放水果
List<? super Numder> = new ArrayList<Number>();
list.add(43);   // 运行成功,泛型直接持有了具体的类型信息

List<? extends Number> = new ArrayList<Number>();
list.add(43)   // 直接编译期间给出错误


public static <T> cout( List<? super T>, T item ) {}    //调用   cout( new ArrayList<Number>, 43 );     没问题 

  • 无界通配符 ? —— 捕获

    1. 泛型内泛型,注意放变量跟初始化一样 —— 嵌套初始化泛型需一模一样
class Father<T>{}
List< Father<?> > list = new ArrayList< Father<?> >()   //无界匹配符不能填写确定的符号
错误: List< Father<?> > list = new ArrayList< Father<String> >()   //编译器错误
list.add( Father<String> ); //只有在只能一个泛型时才能改成确切的类型

    2. 一旦被赋值无界通配符,这个符合就仿佛捕获了确切的参数类型 (编译期)

List<?> a = new ArrayList<Integer>();
public static <T>  void test( List<T> list) {}  
public static <T> void test( List<T> list, T item ) {}
test(a)     //运行成功
test(a, new Integer(5) )   //不能运行,知道捕获有实际类型,但又不能得到是实际类型的什么。
参数化接口
  • 不能同时继承同一个泛型接口的的两个变体因为运行时泛型类型并无作用

总之继承的两个泛型变体需要 泛型参数相同

interface One<T>{}
class Two implements One<String> {}
class Three implements One {}
class Four extends Two implements One< Integer> {}     //编译错误,由于类型被擦出并不能识别重写以及继承的类,故错误

class Four extends Two implements One< String>{} // 可以这样编写
class Five extends  Three implements One{}     // 可以这样编写
自限定
  • 新类直接继承泛型类,不过这个继承的泛型的参数类型为该新类 —— 提供了模板
class Temple<T> {
    T item;
    Temple() { item = null; }
    public T get() { return item; }
    public void set(T item) { this.item = item; }
}
class Son extends Temple<Son> {}       //继承了Temple所有方法,不过其边界是新类本身

public static void main(String[] args) {
    Son son1 = new Son();
    Son son2 = new Son();
    son1.set(son2);         //son1 保存了son2对象
    Son son = son1.get();     //得到son1里面的son2对象
    

  • 保证继承的类型参数与正在被定义的类相同 —— 限定了输入输出T类型需要是Father的子类
class Father<T  extends Father<T> > {} //一旦初始化,限定了T必须继承Father<T>的前提
class Son extends Father<Son>{}    //Father充当模板,并且限定Son必须继承Father的前提
class Son2 extends Father<Son>   //这个没错,因为Son的相关继承属性没有被打破

class Son3{}
class Son4 extends Father<Son3>{}   //这条语句表达的意思是 Son3 extends Father<Son3> , 然而实际Son3并没有继承Father  ,故冲突, 编译期错误
混型 —— java不允许(实质是多重继承)

C++混型能拥有所混入的所有方法

template<class T> class Father : public T {}
template<class T> class Son : public T{}
class Basic{}
Father<template<Basic>>  mix    //mixb变量能用 Father、Son、Basic 的方法

替代性的实现混型可用接口 —— 用代理实现

  • 静态代理
interface Name() { String getName(); }
class NameImp implements Name {
    String name = "a";
    String getName() { return name; }
}
interface Basic {
    void set(int value);
    int getId();
}
class BasicImp implements Basic {
    int id;
    public void set(int value) { this.id = value); }
    public int getId { return id; }
}
class Mix extends BasicImp implements Name {    //混型,代理,多重继承
    NameImp ni = new NameImp();
    public String getName() {
        return nI.getName();
    }
}
动态代理 —— 更加贴近混型

由于动态代理的要求:必须要某个接口的实现类实例来调用

class TwoTuple<A, B> {
    A first;
    B second;
    TwoTuple(A first, B second) { 
        this.first = first;
        this.second = second;
    }
}

class MixinProxy implements InvocationHandler {
    Map<Method, Object> proxyMethods;
    //传一个二元对象数组( 二元分别为 接口实例,接口类型信息 )
    MixinProxy(TwoTuple<Object, Class<?>>... args) {
        for( TwoTuple<Object, Class<?>> tt : args) {
            for(Method method : tt.second.getMethods() ) {
                String methodName = method.getName();
                if( proxyMethod.containsKey(methodName) == false )
                    proxyMethod.put( methodName, tt.first );
            }
        }
    }
    //调用方法,从proxyMethods中得到 方法对象    
    public Object invoke( Object proxy, Method  method, Object[] args ) {
        mathodName = method.getName();
        Object object = proxyMethods.get( methodName );
        return method.invoke( object, args);
    }
    //返回一个代理对象
    public static Object newInstance( TwoTuple... args) {
        Class<?>[] interfaces = new Class<?>[args.length];
        for( int i = 0 ; i < interfaces.length; i++) {
            interfaces[i] = args[i].second;
        }
        ClassLoader cl = args[0].first.getClass().getClassLoader();
        Object proxy = Proxy.newInstance( cl, interfaces, new MixinProxy( args) ) ;
        return proxy;
    }
}

潜在类型机制 —— 无需继承,调用不属于该目前类的方法

支持这机制的是C++(静态)、Python(动态)java不支持
该实例对象只要有这个方法,就能调用,不在乎当前的类型缩小方法接口,没有这个方法,则运行报错


折中用反射实现该机制 :

{
//需要调用的方法,假设
    public void speak();
}

public static void invoke (Object object) {
    Class<?> type = object.getClass();
    try {
        try {
            Method speakMethod = type.getMethod("speak");
            speakMethod.invoke(object);
        }catch(NoSuchMethodException e) {
            println(type.getSimpleName() + "no speak method");
        }
    }catch( Exception e) {
       throw new RuntimeException(e);     
    }
}

  • 容器以及有容器的类可公用一个方法,只写一个方法
  • 可静态检查,可多种自定义容器使用
  • 适配器模式

// 用这个接口来泛化 Apply.fill方法
interface Addable<T> {
     public void add(T item) ;
}

//定义通用方法
class Apply {
     public static <T> void fill(Addable<T> addable, Class<? extends T> type, int size){
           try {
                for(int i = 0; i<size; i++) {
                     addable.add(type.newInstance());
                }
           }catch(Exception e) {
               throw new RuntimeException(e);
           }
     }
}

//静态代理Collection中的add方法
class AddableCollectionAdapter<T> implements Addable<T> {
     Collection<T> collection;
     AddableCollectionAdapter(Collection<T> collection) { 
        this.collection = collection; 
     }
     public void add(T item) {
          collection.add(item);
     }
}

//适配上面静态代理的类,用Adapter 静态方法生成该类
class Adapter {
     public static <T> Addable<T> collectionAdapter(Collection<T> collection) {
           return new AddableCollectionAdapter<T>(collection);
     }
}

//单纯只实现了Iterable的自定义组合容器
class SimpleList<T> implements Iterable<T> {
     ArrayList<T> list = new ArrayList<T>();
     public Iterator<T> iterator() {
           return list.iterator();
     }
     public String toString() {
           return list.toString();
     }
}

//适配SimpleList 需要实现Addable接口
class AddableSimpleList<T> extends SimpleList<T> implements 
Addable<T> {
     public void add(T item) {
           list.add(item);
     }
}

适配器、策略模式结合的 —— 接近混型

方法参数为策略接口,传入不同的策略类运行同一方法不同效果

紧耦合
函数对象,仿函数:可以传递出去,拥有多个调用之间持久化的状态

//主类方法需要用到的泛型类
interface Combiner<T> { 
    public T combine(T x, T y);
}
//适配器
public IntegerAdder implements Combiner<Integer> {   
    public  Integer combine(Integer x, Integer y) {
        return x+y;
    }
}

public class  Test {
    public static <T>  T jiehe(Iterable<T> seq, Combiner<T> combiner) {
       Iterator iterator = seq.iterator();
       if( iterator.hasNext() ) {
        T result = iterator.next();
        while( iterator.hasNext() ) {
            result = combiner.combine(result, iterator.next() );
        }
    }  
    
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6);
        System.out.println(Test.jiehe( list, new IntegerAdder() ) );    //output: 21
    }
)