之前一个想法.
就是Java里实现类似go的interface的形式约束.
即可以后期规约出一个interface然后把一个object cast过去.
类似new Anycast().adopt(object).newInstance(Interface.class)的用法.
最简单的方式当然就是Invocationhandler的Proxy方式.
但这里有个问题就是interface的default method.
因为default method的作用可以是用来定义一个control flow template.
比如Stream API的collector.
通过暴露一些可实现的流程细节,在给定的流程框架内适应.
而proxy的实现实际上通过实现给定interface的所有方法做到的.
这也意味这default method被事实上override了.
这里有几种可能的方法.
一个就是实现一个带有default method的instance.
但由于实际上各个abstract method归属的this/method receiver不同.
所以实际上也并不能够达到目的.
另外一个思路是method handler.
因为invocation handler里给出了proxy object的instance.
所以在lookup能拿到method handler的前提下,就能bind到method receiver.
而这个的问题也就正在于lookup的限制性.
并不是像老一辈的reflection API一样是相对很随意的.
lookup对interface和method的访问性约束比其他要严格许多.
正如public要求或者同package之类的.
所以在proxy的框架内,实现还是有一定的局限性.
那么考虑绕过proxy,直接自己生成的方式.
操作bytecode的话就相对简单很多.
对于abstract的,直接stub就行.
而default method跳过便是.
因为本意就是要default.
既然动用到了生成字节码的方式的话,那么就还有另外一个思路.
就是stub的方式.
可以是走invoke dynamic.
这里有一个好处是bind method的时候,从API层面来说会简洁很多.
因为直接findVirtual就可以了.
而且不用为default method做特例处理.
因为default method也是可以直接bind到的.
唯一的问题是invoke dynamic的bootstrap method是没有instance上下文的.
也就是说没有this之类的概念.
理由要理解起来也很简单.
因为method handler更像是一个语言层面的code reference.
invoke dynamic做的是late binding就类似于把相关的代码实现剪切过来.
也就是类似运行时的instruction的copy & paste.
只是代码块的复制的话自然也就没什么this的概念.
因为从calling convention来说,method receiver是第一个push到栈上的参数.
是属于parameter的范畴.
并不在实际的代码逻辑内.
所以在bootstrap method里是找不到这些东西的.
一个折中的办法是通过static field去做dereference.
因为static field的访问是没有什么receiver的概念的.
也就是在bootstrap method里是有办法访问到的.
唯一要考虑的方式就是如何寻址的问题.
因为如果这个generated class是共享的,那么就需要在bootstrap method里找到一个唯一确定的"this"的方式.
比如,如果这个this的寻址是通过一个static map/set来实现的.
就需要保证任何时候这个map/set里只有一个唯一确定的元素.
也就是需要在外部this的注册以及bootstrap的调用的时候考虑锁相关的问题.
还有就是这个map/set里元素的引用处理问题.
因为如果不考虑的话,GC至少会有一个strong reference在这里.
所以这个可能是个比较复杂且不容易对的方案.
另外一个方式是generated class是实际上的singleton.
这样的话static field就是实际上的instance field.
既能够在bootstrap method里resolve.
也能把reference规约到instance本身的生命周期里去.
唯一的问题就是对于这类instance如果有比较多的需求的话.
对应的就会产生很多基本雷同的class.
所以invoke dynamic这系就有一个很微妙的设计上的冲突,或者说问题.
因为它基于的method handler/callsite的概念是相对于面向纯粹指令性的.
并没有太多的语言语法层面的约束定义.
用一个可能不太确切的词形容就是纯functional的,并不是OOP的.
当时要让它在语言或者说runtime层面能够跑起来的话,有必须是依赖classloader去define生效的.
当然,不是说method handler不能直接invoke.
而是method handler的构造离不开class.
就像很诡异的.
method handler对应的原则上来说是一段执行代码.
没有this,没有method receiver,只是纯粹的function.
但是你在语言侧面你又没办法把一段哪怕是static的code block或者method直接转换成method handler.
而还是需要通过lookup的方式去获取.
而lookup本身又非常地具有局限性.
一个比较实际的例子就是anonymous class.
因为anonymous class实际上是final类protected的access.
所以并不是能够随时随地自由地通过lookup转换成method handler.
就像自身的lambda机制.
实际上是通过在当场对每一lambda生成对应的相互独立的invoke dynamic bootstrap handler做到的.
而这,也恰恰就是为什么lambda没有"this"的原因.
因为从语法层面上来说,lambda跟形式类似的anonymous class实际上是完全不同的两个东西.
而且另外一点就是,method handler貌似是没有什么异常概念的.
这个也好理解,因为是code reference.
但是lambda却在语法层面保留了exception.
于是这里就有一个相对分裂的地方.
一方面因为实现的原因,没有this概念.本质上是完全不同的东西,只是具有类型使用形态.
另一方面为了保持折中形态的类似性,引入了从实现层面上来说完全没有意义的exception声明的语法一致性约束.
仿佛就是一种应该保留的没有保留,而不应该保留的偏偏保留了下来的感觉.
所以有时候感觉这是一个很别扭的实现.
一个试图表征纯粹指令集合的东西,却摆脱不了class的约束.
一个本来就是把method receiver当作参数传递的实现,却在callsite里抹掉了痕迹.
一个明明为了实现简明抹去了exception的method handler,却在lambda的语法层面做了保留.
在加上lookup本身的各种限制.
感觉鸡肋了很多.
或者说,给人的感觉是一个比较糟糕的补丁方式.
没有评论:
发表评论