Java中的潜在类型机制 绝地灬酷狼 2022-05-30 07:18 106阅读 0赞 感觉这一周一直在疯狂写博文,效果还是可以,只不过知识来的快忘得也快,好事要多复习才行。 先一句话介绍一下潜在类型机制到底是什么:**潜在类型机制是一种代码组织和服用机制**,简单的解释一下就是:只要多个类有同样的方法,那么就可以在使用这个方法时不关注类型。但准确的来说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>)显示出了潜在类型机制,只不过我觉得就算加上了适配器这种方法也并没有脱离接口和泛型、继承的影响,所以也不见得就有多好。 这篇博文花了两天完成,也算是自己总结了一下,希望大家都能有收获,也欢迎大家互相交流,留言
相关 反射机制滥用:Java代码中反射的潜在风险 反射机制是Java语言提供的一种允许程序在运行时检查类、接口、字段和方法的能力。它具有强大的功能,但过度使用或滥用可能会带来一些潜在风险: 1. 性能影响:反射通常比直接操作 骑猪看日落/ 2024年09月20日 07:12/ 0 赞/ 7 阅读
相关 Java反射机制的理解及其潜在问题 Java的反射机制是一种强大的工具,它允许我们在运行时检查对象的信息、调用方法以及动态创建类等操作。 理解: 1. 类和对象:反射可以获取任何类的信息,包括父类、成员变量、成 末蓝、/ 2024年09月16日 06:03/ 0 赞/ 3 阅读
相关 Java泛型与类型安全:何时滥用泛型导致的潜在风险 Java泛型,也被称为类型参数,是Java 5引入的一种特性,旨在提供类型安全的编程方式。然而,如果滥用泛型,可能会带来一些潜在的风险: 1. 内存消耗:过度使用泛型会导致类 素颜马尾好姑娘i/ 2024年09月10日 15:30/ 0 赞/ 17 阅读
相关 Java反射机制的运用及其潜在的安全风险 Java反射机制是一种强大的工具,它允许我们在运行时检查类、方法和字段等对象的信息。以下是其主要运用: 1. 动态加载:可以动态地创建一个类,并执行初始化操作。 2. 获取 小鱼儿/ 2024年09月04日 12:12/ 0 赞/ 23 阅读
相关 Java中的反射机制:原理、用途及其潜在风险 反射机制是Java语言中一个强大的特性,它允许我们在运行时检查类的信息,动态调用方法,甚至修改字段值。 1. 原理: - 编译期,Java编译器将源代码转化为字节码。 我就是我/ 2024年09月04日 04:30/ 0 赞/ 13 阅读
相关 Java 中的异常类型、异常处理机制、最佳实践 Java 异常是一种在程序运行时可能出现的错误或异常状况。它们可以由多种因素引起,例如无效输入、网络连接失败或系统资源不足等。 Java 提供了内置的异常类和处理机制,以便在 Myth丶恋晨/ 2024年03月16日 13:22/ 0 赞/ 13 阅读
相关 Java中的潜在类型机制 感觉这一周一直在疯狂写博文,效果还是可以,只不过知识来的快忘得也快,好事要多复习才行。 先一句话介绍一下潜在类型机制到底是什么:潜在类型机制是一种代码组织和服用机制,简单的 绝地灬酷狼/ 2022年05月30日 07:18/ 0 赞/ 107 阅读
相关 go中的别名类型和潜在类型 type MyString = string 这条声明语句表示,MyString是string类型的别名类型。顾名思义,别名类型与其原类型恐怕只是名称上,它们是完全相 浅浅的花香味﹌/ 2022年03月26日 21:34/ 0 赞/ 175 阅读
相关 Java 中 7 个潜在的内存泄露风险 虽然Java程序员不用像C/C++程序员那样时刻关注内存的使用情况,JVM会帮我们处理好这些,但并不是说有了GC就可以高枕无忧,内存泄露相关的问题一般在测试的时候很难发现,一旦 旧城等待,/ 2021年09月07日 06:19/ 0 赞/ 159 阅读
还没有评论,来说两句吧...