感觉这一周一直在疯狂写博文,效果还是可以,只不过知识来的快忘得也快,好事要多复习才行。
先一句话介绍一下潜在类型机制到底是什么:潜在类型机制是一种代码组织和服用机制,简单的解释一下就是:只要多个类有同样的方法,那么就可以在使用这个方法时不关注类型。但准确的来说Java中并没有潜在类型机制,下面直接上代码:
import typeinfo.pets.*;
import static net.mindview.util.Print.*;
public interface Performs
{
void speak();
void sit();
}
class PerformingDog extends Dog implements Performs {
public void speak() { print("Woof!"); }
public void sit() { print("Sitting"); }
public void reproduce() {}
}
class Robot implements Performs {
public void speak() { print("Click!"); }
public void sit() { print("Clank!"); }
public void oilChange() {}
}
class Communicate {
public static <T extends Performs>
void perform(T performer) {
//当然这个地方的泛型参数实际上并不是必须的,因为我们可以对用基类代替具体类比如:public static void perform(Performers performer)不使用泛型同样可以达到效果
performer.speak();
performer.sit();
}
}
public class DogsAndRobots {
public static void main(String[] args) {
PerformingDog d = new PerformingDog();
Robot r = new Robot();
Communicate.perform(d);
Communicate.perform(r);
}
}
/* 输出:
Woof!
Sitting
Click!
Clank!
*/
上面是一段逻辑非常清楚的代码,我们可以看到Communicate类的静态方法perform似乎满足了潜在类型机制,但是实际上我们用了额外的一个接口Performs去实现这个机制,所以我们可以这么说,Java中没有直接可以实现潜在类型机制的方法,只不过通过接口可以模拟实现。
模拟潜在类型机制
使用反射进行模拟
废话不多说直接上代码
import java.lang.reflect.*;
import static net.mindview.util.Print.*;
class Robot
{
public void speak() { print("Click!"); }
public void sit() { print("Clank!"); }
public void oilChange() {}
}
class Mime
{
public void walkAgainstTheWind() {}
public void sit() {print("Mime sit()");}
public void pushInvisibleWalls() {};
public String toString() {return "Mime()";}
}
class SmartDog
{
public void speak(){print("Woof!");}
public void sit(){print("Sitting");}
public void reproduce(){}
}
class CommunicateReflectively
{
public static void perform(Object speaker){
//在perform方法中使用了反射通过找出.getMethod(方法名)方法先判断是否存在该方法,从而进一步通过.invoke(拥有该方法的对象)方法来调用该方法
Class<?> spkr = speaker.getClass();
try{
try{
Method speak = spkr.getMethod("speak");
speak.invoke(speaker);
}catch(NoSuchMethodException e){
print(speaker + "cannot speak");
}
try{
Method sit = spkr.getMethod("sit");
sit.invoke(speaker);
}catch(NoSuchMethodException e){
print(speaker + "cannot sit");
}
}catch(Exception e){
throw new RuntimeException(speaker.toString(),e);
}
}
}
public class LatentReflection{
public static void main(String[] args){
CommunicateReflectively.perform(new SmartDog());
CommunicateReflectively.perform(new Robot());
CommunicateReflectively.perform(new Mime());
}
}
CommunicateReflectively类的perform()方法中注释中已经给出了这种用法的解释。
使用特定接口(序列化)代替潜在类型机制
为了使用特定接口代替潜在类型机制,我们需要将我们所有想要实现潜在类型机制的类全部实现同一个接口,用下面的代码举一个例子:
import java.lang.reflect.*;
import java.util.*;
import static net.mindview.util.Print.*;
class Apply
{
public static <T, S extends Iterable<? extends T>> void apply(S seq, Method f, Object...args)
{
try{
for(T t:seq){
f.invoke(t, args);
}
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
class Shape{
public void rotate(){print(this + "rotate");}
public void resize(int newSize){
print(this + " resize " + newSize);
}
}
class Square extends Shape{}
class FilledList<T> extends ArrayList<T>
{
public FilledList(Class<? extends T> type, int size){
try{
for(int i=0; i<size; i++){
add(type.newInstance());
}
}catch(Exception e){
//System.out.println(e);
throw new RuntimeException(e);
//RuntimeException标志着程序员的错误
}
}
}
class SimpleQueue<T> implements Iterable<T>{
//自定义实现了Iterable接口的类
private LinkedList<T> storage = new LinkedList<T>();
public void add(T t){storage.offer(t);}
public T get(){return storage.poll();}
public Iterator<T> iterator(){
return storage.iterator();
}
}
public class ApplyTest
{
public static void main(String[] args)throws Exception{
List<Shape> shapes = new ArrayList<Shape>();
for(int i=0; i<10; i++){
shapes.add(new Shape());
}
Apply.apply(shapes, Shape.class.getMethod("rotate"));
Apply.apply(shapes, Shape.class.getMethod("resize",int.class),5);
List<Square> squares = new ArrayList<Square>();
for(int i=0; i<10; i++){
squares.add(new Square());
}
Apply.apply(squares, Shape.class.getMethod("rotate"));
Apply.apply(squares, Shape.class.getMethod("resize",int.class),5);
Apply.apply(new FilledList<Shape>(Shape.class, 10), Shape.class.getMethod("rotate"));
Apply.apply(new FilledList<Shape>(Square.class, 10), Shape.class.getMethod("rotate"));
SimpleQueue<Shape> shapeQ = new SimpleQueue<Shape>();
for(int i=0; i<5 ;i++){
shapeQ.add(new Shape());
shapeQ.add(new Square());
}
Apply.apply(shapeQ, Shape.class.getMethod("rotate"));
System.out.print("ok");
}
}
/*输出:;略长
Shape@6d06d69crotate
Shape@6bc7c054rotate
Shape@232204a1rotate
Shape@4aa298b7rotate
Shape@7d4991adrotate
Shape@28d93b30rotate
Shape@1b6d3586rotate
Shape@4554617crotate
Shape@74a14482rotate
Shape@1540e19drotate
Shape@6d06d69c resize 5
Shape@6bc7c054 resize 5
Shape@232204a1 resize 5
Shape@4aa298b7 resize 5
Shape@7d4991ad resize 5
Shape@28d93b30 resize 5
Shape@1b6d3586 resize 5
Shape@4554617c resize 5
Shape@74a14482 resize 5
Shape@1540e19d resize 5
Square@677327b6rotate
Square@14ae5a5rotate
Square@7f31245arotate
Square@6d6f6e28rotate
Square@135fbaa4rotate
Square@2503dbd3rotate
Square@4b67cf4drotate
Square@7ea987acrotate
Square@12a3a380rotate
Square@29453f44rotate
Square@677327b6 resize 5
Square@14ae5a5 resize 5
Square@7f31245a resize 5
Square@6d6f6e28 resize 5
Square@135fbaa4 resize 5
Square@2503dbd3 resize 5
Square@4b67cf4d resize 5
Square@7ea987ac resize 5
Square@12a3a380 resize 5
Square@29453f44 resize 5
Shape@6e0be858rotate
Shape@61bbe9barotate
Shape@610455d6rotate
Shape@511d50c0rotate
Shape@60e53b93rotate
Shape@5e2de80crotate
Shape@1d44bcfarotate
Shape@266474c2rotate
Shape@6f94fa3erotate
Shape@5e481248rotate
Square@66d3c617rotate
Square@63947c6brotate
Square@2b193f2drotate
Square@355da254rotate
Square@4dc63996rotate
Square@d716361rotate
Square@6ff3c5b5rotate
Square@3764951drotate
Square@4b1210eerotate
Square@4d7e1886rotate
Shape@3cd1a2f1rotate
Square@2f0e140brotate
Shape@7440e464rotate
Square@49476842rotate
Shape@78308db1rotate
Square@27c170f0rotate
Shape@5451c3a8rotate
Square@2626b418rotate
Shape@5a07e868rotate
Square@76ed5528rotate
ok
*/
Apply.apply需要一个实现了Iterable接口的类作为参数,所以如果要使用Apply.apply方法必须实现这个接口,很庆幸的是List正好是实现了这个接口的方法,当然除了List,其他所有的Collection类都可以被当作参数,总结一下,用特定接口(序列化)实现潜在类型机制,是将代码复用从方法提升到了类,只不过仍然需要用到反射的方法。
当我们并不拥有正确的接口
虽然通过序列化可以代替潜在类型机制,总不可能每一次都实现一个容器的封装,看一下下面的例子:
import java.util.*;
class Fill
{
public static <T> void fill(Collection<T> collection,Class<? extends T>classToken,int size){
//注意这里的第一个参数Coleection<T>
for(int i =0;i<size; i++){
try{
collection.add(classToken.newInstance());
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
}
class SimpleQueue<T> implements Iterable<T>{
private LinkedList<T> storage = new LinkedList<T>();
public void add(T t){storage.offer(t);}
public T get(){return storage.poll();}
public Iterator<T> iterator(){
return storage.iterator();
}
}
class Contract
{
private static long counter = 0;
private final long id = counter++;
public String toString(){
return getClass().getName() + "" +id;
}
}
class TitleTransfer extends Contract{}
class FillTest
{
public static void main(String[] args){
List<Contract> contracts = new ArrayList<Contract>();
Fill.fill(contracts, Contract.class, 3);
Fill.fill(contracts, TitleTransfer.class, 2);
for(Contract c : contracts){
System.out.println(c);
}
SimpleQueue<Contract> contractQueue = new SimpleQueue<Contract>();
//Fill.fill(contractQueue, Contract.class, 3);
//上面这一行将无法编译
}
}
请注意Fill.fill方法第一个参数指明了必须要一个Collection容器,再看看最后一行,最后一行无法编译的原因是,contractQueue是一个仅仅实现了Iterable接口的类,并不是存在于Collection继承体中的类,所以无法将他转型为Collection类,所以这一行将无法编译。这就是Java试图去模仿潜在类型机制,但是并不能完全实现的一个证明。
用适配器仿真潜在类型机制
如果要说明这个问题我先解释一下什么叫做适配器模式,举个例子,我们在结婚的时候,我们需要钱,我们需要车需要房子,但是我们的工资只够买个小房子,所以现在很多时候父母都为儿女买了房子买了车,这就是适配器的过程,我简单的用代码表示:
import coffee.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
public interface Marriage
{//结婚要买房买车
void HaveCar();
void HaveHouse();
}
class Parents
{//只有靠父母接济才能买得起房
public void HaveHouse(){};
}
public class poor extends Parents implements Marriage
{//只不过我们的工资买个车还是可以的
public void HaveCar(){};
}
虽然这个适配器看起来很简单,但是结婚是很麻烦的(偷笑)。一句话说明一下适配器的要求就是通过继承满足接口要求。
下面来看看怎么用适配器仿真潜在类型机制:
import coffee.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
//首先我们定义一个咖啡的基类
public class Coffee {
private static long counter = 0;
private final long id = counter++;
public String toString() {
return this.getClass().getSimpleName() + " " + id;
}
}
/*
然后我们定义一串咖啡的子类Latte,Mocha之类的,这个地方为了不让代码太长,所以我不写出来。
*/
//下面就是比较复杂的部分了
interface Addable<T>{ void add(T t); }
class Fill2
{
//将fill方法重载,参数为(实现Addable接口的类,类,大小)
public static <T> void fill(Addable<T> addable, Class<? extends T> classToken,int size){
for(int i=0; i<size; i++){
try{
addable.add(classToken.newInstance());
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
//将fill方法重载,参数为(实现Addable接口的类,实现了Generator的对象,大小)
/*
public static <T> void fill(Addable<T> addable, Generator<T> generator,int size){
addable.add(generator.next());
}
*///很可惜这个方法并没有用上
}
class AddableCollectionAdapter<T> implements Addable<T>
{
private Collection<T> c;
public AddableCollectionAdapter(Collection<T> c){
this.c = c;
}
public void add(T item){
//实现Addable接口中的add方法
c.add(item);
}
}
class Adapter
{
public static <T> Addable<T> collectionAdapter(Collection<T> c){
return new AddableCollectionAdapter<T>(c);
}
}
class SimpleQueue<T> implements Iterable<T>{
private LinkedList<T> storage = new LinkedList<T>();
public void add(T t){storage.offer(t);}
public T get(){return storage.poll();}
public Iterator<T> iterator(){
return storage.iterator();
}
}
class AddableSimpleQueue<T> extends SimpleQueue<T> implements Addable<T>
{
//使用适配器模式只不过这个地方还是覆盖了基类SimpleQueue中的add方法
public void add(T item){
super.add(item);
}
}
public class Fill2Test
{
public static void main(String[] args){
List<Coffee> carrier = new ArrayList<Coffee>();
Fill2.fill(
new AddableCollectionAdapter<Coffee>(carrier),
Coffee.class,
3);
Fill2.fill(
Adapter.collectionAdapter(carrier),
Latte.class,
2);
for(Coffee c: carrier)
print(c);
AddableSimpleQueue<Coffee> coffeeQueue = new AddableSimpleQueue<Coffee>();
Fill2.fill(coffeeQueue, Mocha.class, 4);
Fill2.fill(coffeeQueue, Latte.class, 1);
for(Coffee c: coffeeQueue)
print(c);
}
}
/*输出:
Coffee 0
Coffee 1
Coffee 2
Latte 3
Latte 4
Mocha 5
Mocha 6
Mocha 7
Mocha 8
Latte 9
*/
我们用了一个接口Addable让至少两种类(AddableSimpleQueue<T>,AddableCollectionAdapter<Coffee>)显示出了潜在类型机制,只不过我觉得就算加上了适配器这种方法也并没有脱离接口和泛型、继承的影响,所以也不见得就有多好。
这篇博文花了两天完成,也算是自己总结了一下,希望大家都能有收获,也欢迎大家互相交流,留言