第十五章 泛型

一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类,如果要编写可以应用与多种类型的代码,这种刻板的限制对代码的束缚就会很大。

1、简单泛型

核心概念:告诉编译器想要用什么类型,然后编译器帮你处理一切细节。

1.1 一个元组类库

下面的程序是一个2维元组,它能够持有两个对象:

//: net/mindview/util/TwoTuple.java
package net.mindview.util;

public class TwoTuple<A,B> {
  public final A first;
  public final B second;
  public TwoTuple(A a, B b) { first = a; second = b; }
  public String toString() {
    return "(" + first + ", " + second + ")";
  }
} ///:~

我们可以利用继承机制实现长度更长的元组。从下面的例子中可以看到,增加类型参数是件很简单的事情:

//: net/mindview/util/ThreeTuple.java
package net.mindview.util;

public class ThreeTuple<A,B,C> extends TwoTuple<A,B> {
  public final C third;
  public ThreeTuple(A a, B b, C c) {
    super(a, b);
    third = c;
  }
  public String toString() {
    return "(" + first + ", " + second + ", " + third +")";
  }
} ///:~

为了使用元组,你只需定义一个长度适合的元组,将其作为方法的返回值,然后在return语句中创建该元组,井返回即可。

//: generics/TupleTest.java
import net.mindview.util.*;

class Amphibian {}
class Vehicle {}

public class TupleTest {
  static TwoTuple<String,Integer> f() {
    // Autoboxing converts the int to Integer:
    return new TwoTuple<String,Integer>("hi", 47);
  }
  static ThreeTuple<Amphibian,String,Integer> g() {
    return new ThreeTuple<Amphibian, String, Integer>(
      new Amphibian(), "hi", 47);
  }
  static
  FourTuple<Vehicle,Amphibian,String,Integer> h() {
    return
      new FourTuple<Vehicle,Amphibian,String,Integer>(
        new Vehicle(), new Amphibian(), "hi", 47);
  }
  static
  FiveTuple<Vehicle,Amphibian,String,Integer,Double> k() {
    return new
      FiveTuple<Vehicle,Amphibian,String,Integer,Double>(
        new Vehicle(), new Amphibian(), "hi", 47, 11.1);
  }
  public static void main(String[] args) {
    TwoTuple<String,Integer> ttsi = f();
    System.out.println(ttsi);
    // ttsi.first = "there"; // Compile error: final
    System.out.println(g());
    System.out.println(h());
    System.out.println(k());
  }
} /* Output: (80% match)
(hi, 47)
(Amphibian@1f6a7b9, hi, 47)
(Vehicle@35ce36, Amphibian@757aef, hi, 47)
(Vehicle@9cab16, Amphibian@1a46e30, hi, 47, 11.1)
*///:~

由于有了泛型,你可以很容易地创建元组,令其返回一组任意类型的对象。而你所要做的,只是编写表达式而已。

在上面的程序中,new表达式确实有点罗嗦。本章稍后会介绍,如何利用泛型方法简化这样的表达式。

1.2 一个堆栈类

传统的下推堆栈:

package com.tutorialspoit;

public class LinkedStack<T> {
	private static class Node<U> {
		U item;
		Node<U> next;
		Node() {
			this.next = null;
			this.item = null;
		}
		Node(U item,Node<U> next) {
			this.next = next;
			this.item = item;
		}
		boolean end() {
			return item == null && next == null;
		}
	}
	private Node<T> top = new Node<T>();
	public void push(T item) {
		top = new Node<T>(item,top);
	}
	public T pop() {
		T result = top.item;
		if(!top.end()) top = top.next;
		return result;
	}
	public static void main(String[] args) {
		LinkedStack<String> lss =  new LinkedStack<>();
		for(String t : "Phaser on stun!".split(" "))
			lss.push(t);
		String s;
		while((s = lss.pop()) != null) System.out.println(s);
	}
}

练习: 移除node上的类型参数,并修改linked stack代码,证明内部类可以访问其外部类的类型参数;

package com.tutorialspoit;

public class LinkedStack<T> {
	private  class Node {
		T item;
		Node next;
		Node() {
			this.next = null;
			this.item = null;
		}
		Node(T item,Node next) {
			this.next = next;
			this.item = item;
		}
		boolean end() {
			return item == null && next == null;
		}
	}
	private Node top = new Node();
	public void push(T item) {
		top = new Node(item,top);
	}
	public T pop() {
		T result = top.item;
		if(!top.end()) top = top.next;
		return result;
	}
	public static void main(String[] args) {
		LinkedStack<String> lss =  new LinkedStack<>();
		for(String t : "Phaser on stun!".split(" "))
			lss.push(t);
		String s;
		while((s = lss.pop()) != null) System.out.println(s);
	}
}

需要去掉static,具体原因第十章:嵌套类; 

1.3 RandomList

作为容器的另一个例子,假设我们需要一个持有特定类型对象的列表,每次调用其上的select()方法时,它可以随机地选取一个元素。如果我们希望以此构建一个可以应用干各种类型的对象的工具,就需要使用泛型:

//: generics/RandomList.java
import java.util.*;

public class RandomList<T> {
  private ArrayList<T> storage = new ArrayList<T>();
  private Random rand = new Random(47);
  public void add(T item) { storage.add(item); }
  public T select() {
    return storage.get(rand.nextInt(storage.size()));
  }
  public static void main(String[] args) {
    RandomList<String> rs = new RandomList<String>();
    for(String s: ("The quick brown fox jumped over " +
        "the lazy brown dog").split(" "))
      rs.add(s);
    for(int i = 0; i < 11; i++)
      System.out.print(rs.select() + " ");
  }
} /* Output:
brown over fox quick quick dog brown The brown lazy brown
*///:~

2、泛型接口

生成器:专门负责创建对象的类,是工厂设计模式的一种应用;

现在,我们编写一个实现Generator<Coffee>接口的类,它能够随机生成不同类型的Coffee对象。

public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
  private Class[] types = { Latte.class, Mocha.class,
    Cappuccino.class, Americano.class, Breve.class, };
  private static Random rand = new Random(47);
  public CoffeeGenerator() {}
  // For iteration:
  private int size = 0;
  public CoffeeGenerator(int sz) { size = sz; } 
  public Coffee next() {
    try {
      return (Coffee)
        types[rand.nextInt(types.length)].newInstance();
      // Report programmer errors at run time:
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
  }
  class CoffeeIterator implements Iterator<Coffee> {
    int count = size;
    public boolean hasNext() { return count > 0; }
    public Coffee next() {
      count--;
      return CoffeeGenerator.this.next();
    }
    public void remove() { // Not implemented
      throw new UnsupportedOperationException();
    }
  };  
  public Iterator<Coffee> iterator() {
    return new CoffeeIterator();
  }
  public static void main(String[] args) {
    CoffeeGenerator gen = new CoffeeGenerator();
    for(int i = 0; i < 5; i++)
      System.out.println(gen.next());
    for(Coffee c : new CoffeeGenerator(5))
      System.out.println(c);
  }
} /* Output:
Americano 0
Latte 1
Americano 2
Mocha 3
Mocha 4
Breve 5
Americano 6
Latte 7
Cappuccino 8
Cappuccino 9
*///:~

下面的类是Generetore<T>接口的另一个实现,它负责生成Fibonacci(斐波那契)数列:

public class Fibonacci implements Generator<Integer> {
  private int count = 0;
  public Integer next() { return fib(count++); }
  private int fib(int n) {
    if(n < 2) return 1;
    return fib(n-2) + fib(n-1);
  }
  public static void main(String[] args) {
    Fibonacci gen = new Fibonacci();
    for(int i = 0; i < 18; i++)
      System.out.print(gen.next() + " ");
  }
}
/* Output:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
*///:~

用适配器来使基本类型作为类型参数:

public class IterableFibonacci extends Fibonacci implements Iterable<Integer> {
  private int n;
  public IterableFibonacci(int count) { n = count; }
  public Iterator<Integer> iterator() {
    return new Iterator<Integer>() {
      public boolean hasNext() { return n > 0; }
      public Integer next() {
        n--;
        return IterableFibonacci.this.next();
      }
      public void remove() { // Not implemented
        throw new UnsupportedOperationException();
      }
    };
  } 
  public static void main(String[] args) {
    for(int i : new IterableFibonacci(18))
      System.out.print(i + " ");
  }
} 
/* Output:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
*///:~

遗留问题:

return IterableFibonacci.this.next();这里为什么返回的是Fibonacci的next?

3、 泛型方法

要定义泛型方法,只需将泛型参数列表置于返回值之前,就像下面这样:

public class GenericMethods {
  public <T> void f(T x) {
    System.out.println(x.getClass().getName());
  }
  public static void main(String[] args) {
    GenericMethods gm = new GenericMethods();
    gm.f("");
    gm.f(1);
    gm.f(1.0);
    gm.f(1.0F);
    gm.f('c');
    gm.f(gm);
  }
} /* Output:
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
GenericMethods
*///:~

GenericMethod并不是参数化的,那么如何理解参数化?

public class Cache<T> {
    T value;

    public Object getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
    
}

简单来说,它将 value 这个属性的类型也参数化,这就是所谓的参数化类型。

3.1 杠杆利用类型参数推断

  • 类型推断只对复制操作有效,其他时候不顶用;
//: generics/SimplerPets.java
import typeinfo.pets.*;
import java.util.*;
import net.mindview.util.*;

public class SimplerPets {
  public static void main(String[] args) {
    Map<Person, List<? extends Pet>> petPeople = New.map();
    // Rest of the code is the same...
  }
} ///:~

可进行显式的类型说明:

f(New.<Person, List<Pet>>map());

3.2 可变参数与泛型方法

泛型方法与可变参数列表能够很好地共存:

public class GenericVarargs {
  public static <T> List<T> makeList(T... args) {
    List<T> result = new ArrayList<T>();
    for(T item : args)
      result.add(item);
    return result;
  }
  public static void main(String[] args) {
    List<String> ls = makeList("A");
    System.out.println(ls);
    ls = makeList("A", "B", "C");
    System.out.println(ls);
    ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
    System.out.println(ls);
  }
} /* Output:
[A]
[A, B, C]
[, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
*///:~

 3.3 用于Generator的泛型方法

 利用生成器,我们可以很方便地填充一个Collection,而泛型化这种操作是具有实际意义的:

public class Generators {
  public static <T> Collection<T>
  fill(Collection<T> coll, Generator<T> gen, int n) {
    for(int i = 0; i < n; i++)
      coll.add(gen.next());
    return coll;
  } 
  public static void main(String[] args) {
    Collection<Coffee> coffee = fill(
      new ArrayList<Coffee>(), new CoffeeGenerator(), 4);
    for(Coffee c : coffee)
      System.out.println(c);
    Collection<Integer> fnumbers = fill(
      new ArrayList<Integer>(), new Fibonacci(), 12);
    for(int i : fnumbers)
      System.out.print(i + ", ");
  }
} /* Output:
Americano 0
Latte 1
Americano 2
Mocha 3
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
*///:~

 fill()方法是如何透明地应用于Coffee和lnteger的容器和生成器?

答:

 3.4 一个通用的Generator

 下面的程序可以为任何类构造一个Generator,只要该类具有默认的构造器。为了减少类型声明,它提供了一个泛型方法。用以生成BasicGenerator:

public class BasicGenerator<T> implements Generator<T> {
  private Class<T> type;
  public BasicGenerator(Class<T> type){ this.type = type; }
  public T next() {
    try {
      // Assumes type is a public class:
      return type.newInstance();
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
  }
  // Produce a Default generator given a type token:
  public static <T> Generator<T> create(Class<T> type) {
    return new BasicGenerator<T>(type);
  }
}

 3.5 简化元组的使用

通过重载static方法创建元组:

//: net/mindview/util/Tuple.java
// Tuple library using type argument inference.
package net.mindview.util;

public class Tuple {
  public static <A,B> TwoTuple<A,B> tuple(A a, B b) {
    return new TwoTuple<A,B>(a, b);
  }
  public static <A,B,C> ThreeTuple<A,B,C>
  tuple(A a, B b, C c) {
    return new ThreeTuple<A,B,C>(a, b, c);
  }
  public static <A,B,C,D> FourTuple<A,B,C,D>
  tuple(A a, B b, C c, D d) {
    return new FourTuple<A,B,C,D>(a, b, c, d);
  }
  public static <A,B,C,D,E>
  FiveTuple<A,B,C,D,E> tuple(A a, B b, C c, D d, E e) {
    return new FiveTuple<A,B,C,D,E>(a, b, c, d, e);
  }
} ///:~

4、匿名内部类

泛型可以用于匿名内部类:

 public static Generator<Customer> generator() {
    return new Generator<Customer>() {
      public Customer next() { return new Customer(); }
    };
}

 5、擦除神秘之处

在泛型代码的内部,无法获得任何有关泛型参数类型的信息,这是因为,泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。

//: generics/ErasedTypeEquivalence.java
import java.util.*;

public class ErasedTypeEquivalence {
  public static void main(String[] args) {
    Class c1 = new ArrayList<String>().getClass();
    Class c2 = new ArrayList<Integer>().getClass();
    System.out.println(c1 == c2);
  }
} /* Output:
true
*///:~

5.1 迁移兼容性

擦除的核心动机是它使得泛化的客户端可以用非泛化的类库来使用,反之亦然,这经常被称为“迁移兼容性”。

5.2 擦除问题

  • 擦除主要的正当理由是从非泛化代码到泛化代码的转变过程,以及在不破坏现有类库的情况下,将泛型融入Java语言。
  • 泛型不能用于显式地引用运行时类型的操作之中,例如转型、instanceof操作和new表达式。

6、擦除的补偿

正如我们看到的,擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都将无法工作:

//: generics/Erased.java
// {CompileTimeError} (Won't compile)

public class Erased<T> {
  private final int SIZE = 100;
  public static void f(Object arg) {
    if(arg instanceof T) {}          // Error
    T var = new T();                 // Error
    T[] array = new T[SIZE];         // Error
    T[] array = (T)new Object[SIZE]; // Unchecked warning
  }
} ///:~

6.1 创建类型实例

如何用泛型创建类的实例?

  • 创建工厂类:
//: generics/FactoryConstraint.java

interface FactoryI<T> {
  T create();
}

class Foo2<T> {
  private T x;
  public <F extends FactoryI<T>> Foo2(F factory) {
    x = factory.create();
  }
  // ...
}

class IntegerFactory implements FactoryI<Integer> {
  public Integer create() {
    return new Integer(0);
  }
}    

class Widget {
  public static class Factory implements FactoryI<Widget> {
    public Widget create() {
      return new Widget();
    }
  }
}

public class FactoryConstraint {
  public static void main(String[] args) {
    new Foo2<Integer>(new IntegerFactory());
    new Foo2<Widget>(new Widget.Factory());
  }
} ///:~
  • 模板方法:
//: generics/CreatorGeneric.java

abstract class GenericWithCreate<T> {
  final T element;
  GenericWithCreate() { element = create(); }
  abstract T create();
}

class X {}

class Creator extends GenericWithCreate<X> {
  X create() { return new X(); }
  void f() {
    System.out.println(element.getClass().getSimpleName());
  }
}    

public class CreatorGeneric {
  public static void main(String[] args) {
    Creator c = new Creator();
    c.f();
  }
} /* Output:
X
*///:~

6.2 泛型数组

 正如第6点的代码,不能创建泛型数组,

可以定义一个泛型数组的引用 T[ ] array ,但无法使用这个引用。

那么有什么解决方案呢?

  • 可以使用ArrayList<T>来代替数组达到相同目的。

//: generics/ListOfGenerics.java
import java.util.*;

public class ListOfGenerics<T> {
  private List<T> array = new ArrayList<T>();
  public void add(T item) { array.add(item); }
  public T get(int index) { return array.get(index); }
} ///:~
  • 内部创建一个Object[ ] 数组,在需要时将数组内的对象转型为需要的类型,但不能将Object[ ]转型为T[ ],因为没有任何方式可以改变数组底层类型。
public class Test<T> {
    private Object[] array={"ji"};
    public T get(int index) {
        return (T) array[index];
    }
    public static void main(String[] args) {
        Test<String> test = new Test<String>();
        String s = test.get(0);// String s = (String)test.get(0);编译器自动插入转型代码
    }
    
}

其实 get内没有任何类型转换 ,T 被擦除成了Object,只是Object转Object了, 创建对象确定T类型后在编译阶段编译器会为你插入转型代码。

7、边界

  • 强制泛型可以使用什么类型
  • 按边界类型调用方法其方法,无边界的只能调用从Objec继承的方法。

7.1 多边界

  • <T extends A & B & C >    A B C 之间没有继承关系
  • 多边界擦除到第一个边界A。

8、通配符

通配符被限定为单一边界;

通配符可以允许某种类型向上转型,与普通边界对比:

  •  List<T extends Fruit> first = new ArrayList<T>();  只能使用T
  •  List<? extends Fruit> first  = new ArrayList<Apple>();  //可以使用各种Fruit的子类。
  • List<? extends Fruit> 读作具有任何从Fruit继承的类型列表。

8.1 区别一个概念 

    • 数组可以向上转型,即导出类型的数组可以赋值给及基本类型数组的引用
    • 而导出类型的泛型确不能赋值给基本类型泛型的引用 如: List<Fruit> list = new ArrayList<Apple>(); 语法错误
    • Apple 是 Fruit 的子类,但  Apple所在的List却不是Fruit所在的List的子类,故不能这样转型。

8.2 上界和下界

<? extends Fruit>  上界 ?是Fruit 的子类,但具体是什么不知道,因此当调用get方法时返回的对象可以赋值给Fruit引用,而add添加对象时由于不清楚具体要添加什么子类所以无法使用add方法。

<? super Apple >  下界   也称 逆变 ?是Apple的父类,但具体是什么类型不得而知,因此当调用add方法添加对象时可以添加Apple和其子类对象,但调用get方法时无法确定要返回什么类型,因此不能调用get方法返回具体类型,只能返回Object。 

8.3 无界通配符 <?>

  • 表示任何类型,相当于使用原生类 ,他还意味着声明 “我要在这里使用泛型”
  1. 原生类List实际上是List<Object>,表示持有Object对象的原生类,而List<?>是利用泛型给List持有的对象划了一个具体的范围,虽然范围也是Object,但List<?>确不是原生类。

8.4 <?>与上下界之间的区别

  1. 一个方法的参数的类型如是 List ,List<?> ,则可以接收任何形式的List参数,参数是不是泛型无所谓。
  2. 参数的类型如果是List<? extends/super A >  ,则只能接收泛型的List参数.
  3. 如果参数的类型是 <?> 或者 <? extends A>,则该方法无法调用
  4. <?>可以向上转型
  5. 多个泛型参数下只有全为?时编译器无法与原生类区分,但只要有过一个参数不是?就会有所区分如Map<String, ?>必须传入map<String,?>类型的参数,而Map<?,?>可以传入new HashMap();

8.5 捕获转换

  • 向一个<?>方法传入有个原生类型,编译器可以捕获实际类型参数,这个方法可以回转并调用另一个使用这个确切类型的方法。
//: generics/CaptureConversion.java

public class CaptureConversion {
  static <T> void f1(Holder<T> holder) {
    T t = holder.get();
    System.out.println(t.getClass().getSimpleName());
  }
  static void f2(Holder<?> holder) {
    f1(holder); // Call with captured type
  }    
  @SuppressWarnings("unchecked")
  public static void main(String[] args) {
    Holder raw = new Holder<Integer>(1);
    // f1(raw); // Produces warnings
    f2(raw); // No warnings
    Holder rawBasic = new Holder();
    rawBasic.set(new Object()); // Warning
    f2(rawBasic); // No warnings
    // Upcast to Holder<?>, still figures it out:
    Holder<?> wildcarded = new Holder<Double>(1.0);
    f2(wildcarded);
  }
} /* Output:
Integer
Object
Double
*///:~

捕获转换只有在这样的情况下可以工作:即在方法内部,你需要使用确切的类型。注意,不能从f2()中返回T,因为T对于f2()来说是未知的。捕获转换十分有趣,但是非常受限。

9、问题

  • 任何基本类型都不能作为类型参数如<T>  T不能是int 可以使用包装器类;
  • 自动包装机制不能应用于数组;
  • 一个类不能实现同一个泛型接口的两种变体;
interface Payable<T> {}

class Employee implements Payable<Employee> {}
class Hourly extends Employee
  implements Payable<Hourly> {} 
  • 带有泛型类型参数的转换或者使用instanceof判断类型都没有任何效果;
  • 由于擦除原因,重载方法将会产生相同的类型签名,这将使得编译器无法编译;

10、自限定类型

强调泛型当作自己的边界来使用:

class selfbounded<T extends selfbounded<T>> { // ... }
  • 自限定限制只能强制作用于继承关系;
  • 可以产生协变参数类型——方法参数类型会随子类的变化而变化;
  • 如果不使用子限定,将重载参数类型,反之,只能获得某个方法的一个版本,他将接受确切的返回类型;

11、混型

混合多个类的能力以产生一个可以表示混型中所有类型的类,那么如何实现混型?

①  代理机制

步骤:1、创建混型接口  2、创建接口的具体实现  3、创建混型类,类中持有混型。

首先 创建混型接口

public interface Fly{
    void fly();
} 

其次,创建接口的具体实现

public Swing implements Fly{
    public void fly(){
      System.out.println("I can use swing to fly"); 
    }
}  

最后创建具体混型类

public class Duck implements Fly{
    private Swing mSwim = new Swing();
    
    public void fly(){
        mSwim.fly();
    }
    
    public void swim(){
        System.out.println("I can swim");
    }
}

Duck

缺点:当使用复杂的混型,代码量会急剧上升。

 

②  装饰器模式

步骤:1、创建被装饰对象(可以是类也可以是接口,最好是接口 这里我用的是接口) 2、创建被装饰对象继承装饰接口  3、创建装饰器(装饰器中包含装饰对象)

1、创建装饰接口

public interface Basket {
    void show();
}

2、继承装饰接口,为被装饰对象

public class Original implements Basket{

    @Override
    public void show() {
        // TODO Auto-generated method stub
        System.out.println("I am a Basket");
    }

}

3、创建装饰器(Apple,Banana,Orange)

 

public class AppleDecorator implements Basket{
    private Basket mBasket;
    public Apple(Basket basket) {
        // TODO Auto-generated constructor stub
        mBasket = basket;
    }
    @Override
    public void show() {
        // TODO Auto-generated method stub
        mBasket.show();
        System.out.println("Apple");
    }
}
public class BananaDecorator implements Basket{
    private Basket mBasket;
    public Banana(Basket basket) {
        // TODO Auto-generated constructor stub
        mBasket = basket;
    }
    @Override
    public void show() {
        // TODO Auto-generated method stub
        mBasket.show();
        System.out.println("Banana");
    }
}

其他略

4、使用装饰器

 

public class Main {
    public static void main(String[]args){
        Original original = new Original();
        Basket basket = new Apple(new Banana(new Orange(original)));
        basket.show();
    }
}

 

缺点:只有效作用于装饰的最后一层。

③ 动态代理

步骤:1、创建接口  2、创建接口的具体实现  3、创建Proxy类 4、使用Proxy类

1、创建接口

public interface Business {
    void doSomething();
    void doSomeElse();
}

2、实现接口

 

public class RealObject implements Business{

    @Override
    public void doSomething() {
        // TODO Auto-generated method stub
        printf("Do some better things");
    }

    @Override
    public void doSomeElse() {
        // TODO Auto-generated method stub
        printf("Anything i can do it");
    }
    
    public static void printf(String str){
        System.out.println(str);
    }

}

 

3、创建代理类

 

public class BusinessProxy implements InvocationHandler{
    private Object mData;
    public BusinessProxy(Object dataObject) {
        // TODO Auto-generated constructor stub
        mData = dataObject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("No one thing I can do");
        return method.invoke(mData, args);
    }
}

 

4、启动代理

 

public class Main {
    public static void main(String[] args){
        RealObject object = new RealObject();
        Business proxy = (Business)Proxy.newProxyInstance(Business.class.getClassLoader(), new Class[]{Business.class},new BusinessProxy(object));
        proxy.doSomething();
    }
}

 

装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

12、潜在类型机制

1、什么叫做潜在类型机制

在C++和python中,可以不知道当前类的类型,就可以调用方法。

①  JAVA如何补偿(反射机制)

1、 建立两个类,其有相同的方法

public class TypeOne {
    private int data = 1;
    
    public void setData(int data){
        this.data = data;
    }
    
    public int getData() {
        return data;
    }
}
  


public class TypeTwo {
    private int data = 2;
    public void setData(int data){
        this.data = data;
    }
    
    public int getData(){
        return data;
    }
}

 2、利用反射调用,相同的方法

public class Main {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TypeOne typeOne = new TypeOne();
        TypeTwo typeTwo = new TypeTwo();
        //调用反射方法,实现不同类可以通过一个方法调用相同的方法
        getData(typeOne);
        getData(typeTwo);
    }
    
    //实现反射的方法
    public static void getData(Object type){
        Class<?> c = type.getClass();
        try {
            //获取叫做getData()的方法
            Method method = c.getMethod("getData");
            //调用该方法
            int data = (int) method.invoke(type,null);
            //输出
            System.out.println(type.getClass().getSimpleName()+"  data:"+data);
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Main

 

 

 

 

缺点:只能在类与类之间使用。

 

②  序列中调用相同的方法

1、创建fill()方法,传入迭代器,通过反射调用序列中对象的方法

public class Full{
      //通过泛型,传入 迭代器,和具体需要调用的方法名,及参数
    public static <T,S extends Iterable<T>> void fill(S sql,Method method,Object...args){
                //获取迭代器
        Iterator<T> iterator = sql.iterator();
        while(iterator.hasNext()){
            T object = iterator.next();
            try {
                                //调用方法
                method.invoke(object, args);
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}            

Full

2、创建调用对象

public class Apple {
    private static int count = 1;
    private final int id = count++;
    
    public void getId(){
        System.out.println(id);
    }
}

Apple

3、使用方法

public static void main(String[] args){
        ArrayList<Apple> list = new ArrayList<>();
        for(int i=0; i<10; ++i){
            list.add(new Apple());
        }
        try {
            fill(list, Apple.class.getMethod("getId"),null);
        } catch (NoSuchMethodException | SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

Full

缺点:只能在含迭代器的序列中使用。

当不存在正确的接口时:

③  在Colleciotn中调用add()方法

同:只是将传入的参数直接变成Collection了而已

④  通过适配器使用潜在类型

1、创建适配的接口

public interface Addable<T> {
    void add(T data);
}

2、创建适配器

public class AddableAdapter<T> implements Addable<T> {
    private Collection<T> collection;
    public AddableAdapter(Collection<T> col) {
        // TODO Auto-generated constructor stub
        this.collection = col;
    }
    @Override
    public void add(T data) {
        // TODO Auto-generated method stub
        collection.add(data);
    }


}

AddableAdapter

3、使用适配器。

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<Integer> list = new ArrayList<>();
        AddableAdapter<Integer> adapter = new AddableAdapter<>(list);
        adapter.add(10);
        for(int i : list){
            System.out.println(i);
        }
    }

}

Main

⑤  通过策略模式一般化潜在类型

1、创建策略接口

2、继承策略接口

3、创建功能类

4、调用

13、将函数对象用作策略

 

可算写完了,只不过还是不太理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值