一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类,如果要编写可以应用与多种类型的代码,这种刻板的限制对代码的束缚就会很大。
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 无界通配符 <?>
- 表示任何类型,相当于使用原生类 ,他还意味着声明 “我要在这里使用泛型”
- 原生类List实际上是List<Object>,表示持有Object对象的原生类,而List<?>是利用泛型给List持有的对象划了一个具体的范围,虽然范围也是Object,但List<?>确不是原生类。
8.4 <?>与上下界之间的区别
- 一个方法的参数的类型如是 List ,List<?> ,则可以接收任何形式的List参数,参数是不是泛型无所谓。
- 参数的类型如果是List<? extends/super A > ,则只能接收泛型的List参数.
- 如果参数的类型是 <?> 或者 <? extends A>,则该方法无法调用
- <?>可以向上转型
- 多个泛型参数下只有全为?时编译器无法与原生类区分,但只要有过一个参数不是?就会有所区分如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、将函数对象用作策略
可算写完了,只不过还是不太理解