duangsuse::Echo
412 subscribers
3.85K photos
105 videos
574 files
5.14K links
duangsuse技术相干订阅
这是 @duangsuse 与技术有关的发布频道
duangsuse 的另外有 throws 闲杂频道
@dsuset
转载频道 @dsusep
duangsuse 有coding,github,gitlab帐号和bilibili帐号

极小可能会有批评zf的消息 如有不适可以退出

suse的小站:https://piped.stream
ps 另有别名 popf.rip
ʕ•̀ω•́ʔ✧ 🐶🍎🏠生死🐜
(>ω<)岂因祸福避趋之 一鿕
Download Telegram
duangsuse::Echo
所以这个人呐,就是要不断的学习,不断的努力提升自己 (当然我不是说 Java 比 Kotlin 简单很多,简单很多是真的,但是很多人连 Java 有 @ label 语法、vararg 语法糖什么的都不会用,标准库的一些语言内部的接口比如 Iterable 都不知道,Annotation 处理机制也不了解,这样的话还是推荐先了解一下 Java 8,推荐自然还是看本本的。 Java 比起 Ruby、Python 这种重视语法灵活性的语言语法规则少一些(不是说语法层面的东西就不值得考虑,我相信在座的各位都没有一个人敢说自己了解…
其实我说的可能是和 Java 一些比较... 不是语法层面的东西吧(我好歹看书找了一下) #Java #Android #PL

呃... 比如 Java 方法(类们的)区、堆、虚拟机栈大小、动态扩容什么的,JDK 命令行工具、JDWP、OQL、Object 类一些基础的东西 #clone() #equals() 什么的,String#intern()、本地表(locals)、并发垃圾收集、JIT 解释混合模式、字节码校验、原生类型装拆箱、动态方法分派(java.lang.mhinvokedynamic,我想后面一个『名词』很多 Android 开发者都听说过,可未必所有人都知道这是干什么的... 你们大概只知道『Dalvik 没有它所以 Lambda 表达式就不能原生实现,只好 runtime desugar』,这个我下面简单讲一下,算是科普)、虚方法、特殊方法、静态方法、接口方法分派逻辑(invokevirtual、invokespecialinvokestaticinvokeinterface)、位运算和各种在计算机数字逻辑上面的东西什么的、泛型擦除、ClassLoader 和字节码技术诸如 CodeGenerationLib、ASM、JavaAssist、BytecodeEngineeringClassLibrary 什么的(总觉得我提这个就很 #low...)、还有最后 Java 运行时一些线程同步机制、安全区、绿色线程、协程什么的...

下面我就兑现诺言讲一下 Java 里(还有点 Kotlin 内容)方法分派之类的东西( #Java #PL #JVM #Kotlin

至于这个计算机科学上函数(类 C 的那种,或者叫子程序)概念... OOP 里的方法、继承、抽象那些基础的东西我就不讲了(因为我不会(逃跑

(虽然的确是个很长的内容... 难受,因为内容比较多...
(以下内容涉及一些基本面向对象编程范式的理论,如果你是为了学习而来的(而不是作为帮我找茬的 dalao)不喜欢不要喷我哦(跑

就目前的 JDK 8 (Java 8)来看,貌似有 5 种方法调用派发逻辑

+ invokestatic 用来调用静态方法,这是最开心的
+ invokespecial 用来调用特殊方法如 constructor <init>(类似 Ruby 里的 initialize),私有方法和父类方法
+ invokeinterface 用来调用接口方法,可能类似 Rust 的 trait object?会在运行时再确定实现此接口的对象
+ invokevirtual 用于调用虚方法
+ invokedynamic 在运行时解析引用的方法,分派逻辑交给用户去实现

至于什么是方法分派呢,我想给大家举个栗子理解一下除了 invokedynamic 的

interface Flyable {
public void fly(); // 公有接口方法
private void __imp__Fly(); // 私有接口方法
}

class Foo {
public Foo() {} // 公开类构造器(架构器)
public static void yoo() {} // 一个公有的静态方法
private void bar() {} // 私有的方法
protected void internal() {} // 受保护的方法
public void pub() {} // 公有的方法
}

invokestatic 和 invokespecial 逻辑都可以在编译过程的语义分析阶段确定他们所引用的方法量词(全限定符)
就是用来调用 静态方法、私有方法、实例构造器、父类方法

== 至于私有方法和公有方法为什么有区别,为什么不用考虑 Reflect 的 case 呢?

首先我们看看面向对象四大特性,『封装』、『继承』、『多态』、『抽象』

私有方法不是虚方法,所谓虚方法(面向对象术语)就是可以被子类继承和覆盖的方法,它是面向对象『多态』特性最重要的组成成分(给出定义了!) wiki

简言之,虚函数可以给出目标函数的定义,但该目标的具体指向在编译期可能无法确定。

比如我们有父类 FooOrBar,其一子类是 Foo 一子类是 Bar,含有一非 final 的公有方法 biz()
有一表象类型为 FooOrBar 的本地变量或方法参数(在 Java 的语义层面)
我们没法在编译期确定语义调用目标对象的 obj.biz() 方法是说在接受者 obj 上分派 Foo#biz() 呢还是 Bar#biz()
这就是 invokevirtual

而公有方法就不能确定是不是非虚方法,猜猜为什么。
Reflect 是程序员自己决定分派逻辑,和虚拟机内建的分派逻辑无关

== 至于 final 方法和虚方法又是什么呢?

Java 里一个类成员被标记为 final 就是说它的子类不可以覆盖(override)这个成员
(btw. Kotlin 里默认所有类都是封闭类 sealed class,就是说不可被继承,Kotlin 里实例方法成员也如此,需要用 open 标记来允许子类 override

final
方法肯定不是虚方法,上面的上面我给定义了

== Java 8 里的 Lambda 表达式语义是什么呢?为什么它需要 invokedynamic 以前的 Anonymous Class 就不需要?

Oracle 爸爸的文档

One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear. 
Lambda expressions let you express instances of single-method classes more compactly.

怎么说呢,对于系统了解过 Ruby 解释器基础的人(知道 Ruby 中有 Lambda、Proc、Method、Block 四种『可调用』对象的人)来说,Java 里的 Lambda 表达式可能类似于 Block,因为它貌似更不像一个独立需要有自己栈帧的子过程

Oracle 官方的说法是 Lambda 表达式是对 SAM (Single Abstract Method)接口方法覆盖的一种语法糖,比如 Runnable 这种只有一个 run() 方法的,作为对匿名子类的替代

因为这方面(为什么 Lambda 表达式要用到 invokedynamic 这种看似好像无关的东西实现)我也不是很清楚(因为其实我也是比较菜的)所以很难讲,因为我也懒得拖太久时间(比如花时间写 examples、看字节码)
StackOverflow[origin] 真香 那么看这个问题,

Lambdas are not invoked using invokedynamic, their object representation is created using invokedynamic, the actual invocation is a regular invokevirtual or invokeinterface.

// creates an instance of (a subclass of) Consumer
// with invokedynamic to java.lang.invoke.LambdaMetafactory
something(x -> System.out.println(x));

void something(Consumer<String> consumer) {
// invokeinterface
consumer.accept("hello");
}

Any lambda has to become an instance of some base class or interface. That instance will sometimes contain a copy of the variables captured from the original method and sometimes a pointer to the parent object. This can be implemented as an anonymous class.

Why invokedynamic

The short answer is: to generate code in runtime.
#Java #JVM 虽然是很垃圾的东西,因为写了一点也拿过来分享一下,算是低端科普。
#Kotlin #backend #JVM #Java 但我肯定我的思路是没问题的,很清晰,但是我没有在可以接受的时间、可以接受的抄代码/记忆量内完成... 成功反驳了对脑力有限论调加以批判的年轻人 🐸 #Moha 好暴力
#Java #Kotlin #JVM #CS #PL #dev 🤔

duangsuse 发现自己对 Java 在集合类里泛型问题的又有了新一层的理解。

为什么容器类需要用泛型?

因为肯定是希望 List<Animal> 能够去 addAll(List<Cat>) 的呢。
Java 里默认的泛型形变,是 Invarant(不变) 的
这种情况下,虽然我们知道 Cat 是 Animal,但是 List<Cat> 却和 List<Animal> 不存在子类型 subtyping 关系!
如果我们要编写一个 addAll(??? other) 函数,保证类型安全(List<Cat> 显然不应该保存 List<Animal>!),并且让 Java 的类型检查器可以做检查的话,就必须指定泛型型变!

在这里,根据 Java 的 PECS(Producer-Extends-Consumer-Super)原则,我们知道 addAll 的类型,显然应该是 void addAll(List<? extends T> input);
但是为什么呢? 🤔

这里你只会了解到和程序设计语言类型有关的信息,Kotlin 的类型投影什么的不会讲。


泛型的『型变』是什么?
泛型的型变就是告诉 Java 类型检查器,一种泛型类的子类型关系,和它的参数化类型有什么样的联系。
这样,我们就可以实现类型安全的容器类泛化类型操作。

+ 协变(covariant):Cat 是 Animal,List<Cat> 也是 List<Animal>
+ 逆变(contravariant):Cat 是 Animal,List<Animal> (这时可能被用作输出) 是 List<Cat>
+ 不变:无论 Cat 是不是 Animal,List<Cat> 和 List<Animal> 之间没有太大的关系

在这里,子类型的『级别』,父类型为小子类型为大,以 lessThan 序排行,可能类似这样:

1. Object(kotlin.Any)
2. Animal
3. Cat

Kotlin 里面,泛型的 in T 指定了可取类型的下界 — 可选类型 ? 必须是 super TT 的超类)才可以,而 Animal 的确 superCat,所以可取类型必须 (lessThan Cat)
它实现了泛型的逆变 —

有类型 P 是类型 C 的父类型,P lessThan C
类型 F<? super C>F<P> 的父类型 (_<P> lessThan _<? lessThan C>)

Kotlin 里面,泛型的 out T 指定了可取类型集合的上界 — 可取类型 ? 必须是 extends TT 的子类型)才可以,而 Cat 的确 extends fromAnimal,所以可取类型必须 (greaterThan Animal)
它实现了泛型的协变 —

有类型 P 是类型 C 的父类型,P lessThan C
类型 F<? extends C>F<P> 的子类型 (_<P> greaterThan _<? greaterThan C>)

其实我们想说,为了类型安全,List<Animal> 的容器里,可以放的东西类型应该是:

? extends Animal
也可以说是 ? greaterThan Animal,反正比 Animal 能就可以了(跑)

我们希望所有对象都的确实现了 Animal(可以装 Animal 和它的子类扩充)
所以,我们需要一个 producer — List<? extends Animal> 来喂数据,进行 addAll 操作(利用一个 Iterable<T>,这里 T 就肯定是某种 Animal, 或者说我们利用 T get(); 方法肯定会拿到 Animal

反过来说,popAll 呢?

PECS,我们知道现在该用 in T🤔

fun popAll(output: Collection<in T>): Unit

或者说,

void popAll(Collection<? super T> output);

我们不希望任何容器,包括 output 被放上不好的东西(某种不是 Animal 的东西!)
所以,当『输出到集合(使用 void add(T item); 方法)』的时候,不如反转一下子类型关系!(相对于 addAll
这样,output(装着某种 T) 就只会被 add 上一些 extends T 的东西(这个类型自己),保证了类型安全

这就使用了泛型的逆变
duangsuse::Echo
可惜没有绘制成图表 plot 好看一些 🤔 r.size #=> 187 一共统计了 187 条消息。 r.sort_by { |it| it['published'] }.first['published'] => 2019-03-24 11:22:00 +0800 第一条消息是 2019-03-24 11:22 发送的 r.sort_by { |it| it['published'] }.last['published'] => 2019-04-05 20:06:00 +0800 最后一条消息是…
😃 那么,简而言之,我觉得有价值的信息:

def get_link(h); "https://t.me/dsuse/#{h['debug']['no'] + 9511 + 20}"; end

+ 一共统计了 187 条消息。

2019-03-24 11:22 - 2019-04-05 20:06,一共两个星期的时间里,本频道 @dsuse

+ 有 #Telegram Hashtag 的消息,一共有 42 条,约有 20% 的消息被打上了标签
+ 含链接的消息,一共有 37 条,约有 19% 的消息被打上了标签
+ 链接和 Tag 都有的消息有 17 条,占总消息的 9%

map { |h| [h['body'].size, h] }.sort_by { |it| it.first }.reverse
map { |h| [h['body'].size, h] }.sort_by { |it| it.first }.reverse.first[1]['links'].size

+ 187 条消息里,最长的消息是这条,它有 4475 个字 — 连链接都有 25 条! 🤪
+ 187 条消息里,折行最多的消息是这条,它有 4232 个字和 7 条链接。

sum { |it| it['body'].size } / size
+ duangsuse 的平均字数:359 字 / 消息

sum { |it| it['body'].lines.size } / size
+ duangsuse 的平均行数:11 行 / 消息

find_all { |it| it['header_type'] == 'REPLY' }.size
+ 过去的 187 条消息里,有 70 条都是回复,占总量 37%

find_all { |it| it['header_type'] == 'FORWARDED' }.size
+ 过去的 187 条消息里,有 45 是转发自其他频道或个人的,占总量 24%
当然,这 70 条都是回复本频道消息的。

a.uniq.map { |u| [u, a.count(u)] }.sort_by { |it| it.last }.reverse.to_h.each { |e| puts "#{e.first}: #{e.last}" }
+ 转发者和条数表如下:

IT 那点事 (YuutaW 鱼塔): 10
羽毛的小白板: 10
duangsuse Throws: 6
Rachel 碎碎念 (IFTTT): 5
Rachel 的消息发布站点 (Rachel Miracle.) via @like: 3
YSC 的频道: 2
duangsuse ¯\_(ツ)_/¯ |学渣 | 我爱学习 | ∈ [E²PROM, 范畴论]: 2
Doge: 2
YuutaW 鱼塔: 2
Rachel 碎碎念 (湘江一桥): 2
DogeSpeed广播: 1

😄 @haneko_daily 被转发的次数最多,继续努力!

sort_by { |it| it['hashtags'].size }.reverse
+ 具有最多标签的消息是这条,它有足足 8 条标签!

sort_by { |it| it['links'].size }.reverse.first
+ 具有最多链接的消息是这条,它有 25 条链接!同时也是字数最多的消息!

find_all { |it| it['header_type'] == 'A_PHOTO' }.size
过去 187 条消息里,一共有 29 条广播是单纯的一个照片 🖼
find_all { |it| it['header_type'] == 'A_ALBUM' }.size
过去 187 条消息里,一共有 5 条广播是照片集 📸
find_all { |it| it['header_type'] == 'IS_STICKER' }.size
可爱 🐱 的 duangsuse 在过去 187 条消息里使用了 28 个 sticker 抒发自己的感情,使用的表情这么多:
find_all { |it| it['header_type'] == 'IS_STICKER' }.collect { |it| it['ext'] }.yield_self { |r| r.uniq.map { |u| [u, r.count(u)] }.sort_by { |it| it.last }.reverse.to_h.each { |e| puts "#{e.first}: #{e.last}" } }

😔: 7
😳: 4
😲: 3
😡: 2
😑: 2
🙄: 2
😋: 2
😀: 2
😥: 2
🍹: 1
😐: 1

find_all { |it| it['header_type'] == 'HAS_FILE' }.size
duangsuse 在过去 187 条消息里发了 8 个文件,它们是:
find_all { |it| it['header_type'] == 'HAS_FILE' }.collect { |it| it['ext'] }
["axml.rb", "AndroidManifest.xml", "AndroidManifest.xml", "TextSort.kt", "RandomPicture.kt", "Vibrator.svg", "Vibrator.png", "Vibrator.svg"]

最火的
hashtags 组合:

collect { |it| it['hashtags'] }.yield_self { |r| r.uniq.map { |u| [u, r.count(u)] }.sort_by { |it| it.last }.reverse.to_h.each { |e| puts "#{e.first}: #{e.last}" } }

结果太长,在这里查看

所有 Hashtags:
hs = s.flatten.find_all { |it| it.is_a? Array }.flatten.map { |s| s.tr(" ", "") }
"#" + hs.uniq.join(' #')

#Android #zhihu #KDE #Low #Haha #life #tech #tencent #WeChat #weibo #share #tools #dev #Markups #Telegram #Hack #aop #Huawei #通知 #Freedom #sysadmin #GitHub #travis #CI #Sysadmin #Linux #Coolapk #Web #frontend #HTML #China #school #Microsoft #Life #VisualStudio #-} #OOP #web #geekapk #doge #CS #fix #CSharp #School #blog #recommended #bin #backend #Kotlin #Java #Share #svg #blogPOst #PL #JVM #Moha #code #ALgotithm #DuangsuseSB #GeekApk #SQL #Learn #wiki #Mozilla #ES6 #JavaScript #MoHa #Paper #haha #dotnet #bad #OOP_Delegates #Parallelism #dotNet #Csharp #Windows #performance #Channel #weekly #linux #Ruby #Project

以及他们的消息覆盖个数:
hs.yield_self { |r| r.uniq.map { |u| [u, r.count(u)] }.sort_by { |it| it.last }.reverse.to_h.each { |e| puts "#{e.first}: #{e.last}" } }

太长,这里看

== 非线性查询(当然和算法上那个没有关系啦,就是复杂一点的查询)

// 收集所有单 / 双数索引

ar = []; ar2 = []

i = 0 // 0 2 4 6
while i < self.size; ar << self[i]; i += 2; end
i = 1 // 1 3 5 7
while i < self.size; ar << self[i]; i += 2; end


aa = ar.zip(ar2)

我们将比较相邻两消息之间的关系。

aa.zip(aa.map { |ms| ms.last['published'] - ms.first['published'] }).sort_by { |p| p.last }.reverse

间隔最长的两条消息是这条这条,间隔 44880 秒(12 小时)

b.map { |h| [h.last, h.first.first['body'].size ] }.map { |a| a.last / a.first }.select { |it| not it.nan? and not it.infinite? }.sort.reverse

平均打字速度:7 字 / 秒 🤔

受到解析度(只到分钟)和浮点运算准确度的影响,有很多消息都是在间隔一分钟内发完的,统计结果可能不正确。

好啦,还有什么别的信息,请大家自己来发掘呗( 😝 比如说,我熬夜发过多少消息。
#Java #JVM 所以喜欢 Jawa 的同学们一定要注意好好学习 Java 哦!

什么是未初始化,什么是空,primitives 的默认值是什么、何时会自动初始化、何时自动装箱、Lambda 到底是什么,得分清楚

就和零基数组一样,Java 程序设计语言这么设计也是有自己的道理的,如果你看字节码,你就会发现这么做的确是符合所谓优雅性的,而不应该去做反直觉的『语法糖』
我之前还以为是黑科技虚拟机层面提供服务支持呢... 说起来我想的和实际的差别可能就是缓存而已,然而其实动态字节码生成已经是一个足够好的方法了。 #Java #JVM
https://github.com/wkgcass/Latte-lang#%E4%B8%AD%E6%96%87%E7%89%88-chinese-version-readme #recommend #plt #jvm Latte ,记住了. Java6 的class编译实现

zig,red; scala.js RustPython moonscript nelua jsweet java2script
https://zserge.com/posts/jvm/ 这个是 #go #bin 实现读classfile常量池&栈执行
https://github.com/axkr/symja_android_library #math #android #tool 符号代数/绘图工具
https://github.com/JacksonTian/stone-lang/tree/master/src/chap12 适合小白读物配套(确信
https://github.com/enso-org/enso 图形编程,可视化, enso.org
https://vlang.io/ 类Go C目标语言,默认no-mut,支持内部序列化. 作者貌似做了C的双向翻译
https://github.com/lclang/LittleCat 用法不错,支持闭包
https://github.com/tern-lang/tern 含游戏示例. 文档太细节
livescript.net 含IDE插件示例
https://github.com/tonysparks/leola 支持协程, namespace: 是啥鬼

https://github.com/cqfn/eo 奇奇怪怪的XML语言.. jolie-lang.org 奇奇怪怪的 Swagger??


https://www.zhihu.com/question/27831730 RednaxelaFX大在这里手撕 #java 编译与反编译器,🐮
你R大永远是你R大🤪

https://www.zhihu.com/question/39400401 还是R大谈CLR-JVM=真泛型&in-out,delegate,P/Invoke&unsigned&Valtype,unsafe和受检溢出,无classloader 树 ,其他人就知道tailrec和jit,gc这些还说混了

不愧是R大 真不是盖的
一己之力拉高 #zhihu 编译原理水平
duangsuse::Echo
The Java maintainers chose to generate the implementation class in runtime. This is done by calling java.lang.invoke.LambdaMetafactory.metafactory. Since the arguments for that call (return type, interface, and captured parameters) can change, this requires…
然后呢华为方舟也是做了个 jbc2mpl && mplcg 的示例,其实主要是移植 #jvm 的感觉, virtualcall (invoke-virtual, invoke-special/static 是普通函数) 方法查找模式都复刻了,也有些if,while 的高级Node,但许多库..哪怕 javacore/libjavacore.mplt 的 rt.jar 和 j.l.String 都没开源,在 #zhihu https://www.zhihu.com/question/343431810 可以看到

#java #learn 这个 invokedynamic 呢,暴露为 j.l.invoke.CallSite ,咱看看用法
#code
class FnRun{
interface R{void run();}
static R f1;
static public void main(String[]a){
f1="".isEmpty()? ()->{ a[0]=""; } :new R(){public void run(){a[1]="";} };
}
}
javap -c FnRun FnRun$R
""?是为了规避常量折叠。无论多复杂,带><转型的常量表达式 sun javac 都是能执行的..妈耶,在框架限制内你可牛

会得到自带的FnRun.<init>=super() 和 main():
..我们用 #python #code 重构一下字节码表示
//L5 "".isEmpty
ldc String
Fvirt String.isEmpty:()Z
5^ifeq //br.not
aload_0
Fdyna #,0:run:([String;)LFnRun$R; //重点!
14^goto
new FnRun$1 ^5 //here
dup
aload_0
Fspec FnRun$1."<init>":([String;)V
putstatic f1:LFnRun$R; ^14 //goto
//L6
return

在非static 方法里 aload_0=this 。在这里 new FnRun$1(a) 被翻译为 new, dup, (a) Fspecial, putstatic 即 {R o=new R; init(o); f1=o}, o值引用了两次
所以Java闭包为啥不能改 ()->{a=new String[]{};} ,在()->{}外也不行? —因为它就是个构造器,Kotlin 使用 IntRef 化来实现完整的局部量共享。

因为代码一样 Fdyna 直接初始化了 FnRun$R.run ,然后 callsite$0 开 javap -v 才能看到,待会说

然后new R(){}的
class FnRun$1 implements FnRun$R {
final java.lang.String[] val$a;
//<init>: L5
aload_0
aload_1 ; putfield val$a:[String;
aload_0
Fspec Object."<init>":()V
return
//run:L5
aload_0 getfield val$a:[String;
iconst_1
ldc String#3
aastore //还有iastore fastore..IntArray啥
return
Java8的动态调用不止是新增opcode
j.invoke.MHs.lookup().findVirtual(Obj,"hashCode",methodType(int))
就是方法句柄
它被 .invoke() 的地方就是动态调用点(callsite) ,bootstrap 方法(^) 用于查找句柄指代的函数实现,只做一次

Fdyna 0:run:([String;)LFnRun$R;
callsite$0 REF_invokeStatic= invoke/LambdaMetafactory.metafactory:(Linvoke/MethodHandles$Lookup;LString;Linvoke/MethodType;
Linvoke/MethodType;Linvoke/MethodHandle;Linvoke/MethodType;)Linvoke/CallSite;

明显这个 metafactory(Lookup,String,MethodType,...) 做了字节码new 的工作,只接受 aload_0(this) 和
FnRun.lambda$main$0:([LString;)V
NameAndType 就创建了内部类 FnRun$R 的对象,这一步只是“调用一个尚不存在的class构造器”,我们负责生成&加载相关类 🤔,并链接-给出代码位置
-Djdk.internal.lambda.dumpProxyClasses=.

但剩下的3参数太多了: SAM methodType(void.class), implMethod, 泛型SAM描述符
没错,invokedynamic 的lookup函数的参大多在编译期确定,甚至不能是 Object 😒

当然用反射newInstance就太容易了,Google dx 使用 RuntimeDesugar 转化^ lambda$ ,当然这都是过去式了, #Kotlin 万岁
super<PT_2>.say()
的自由祖父类指定可以用:

class PT_2 {void say();//parent T
class PT extends PT_2 {}
class T extends PT{void say(){}//新版本
void main(){
MHs.lookup().findVirtual(ParentT_2.class,"say",MT.methodType(void.class)).bindTo(this).invokeExact();
}

你不能手写 invokedynamc 而只能由 j.l.invoke.* 工具类invoke()时生成,因为调用是在字节码层,不是运行期元数据反射 😒

除了查找函数签名的实现,用户代码不会涉及其他关于计算栈的东西,和最常见的 invokevirtual 是完全一样的,而因为lookup的实现有限制, MHs 提供了 collectArguments,guardWithTest,countedLoop 等一系列组合器帮助修改调用,比如加点参数,对Groovy 不定长参 函数转化 这样就很有帮助

顺便: invoke-interface 是劣化的 virtual 。虚方法是单继承得来,成员有唯一索引;接口方法可被多 class implements ,无法做覆写查找的缓存。但能去虚化内联时一样
super()的话是 invokespecial ,不能通过继承被覆盖也无需查找
总之这个Java8引入的 INDY 主要是关于性能前提的,如果以 #js 程序员的视角会觉得这些都不如 new Proxy({}, {get,set}) 😂

我不想再谈了,也没啥用;还不如做JVM脚本语言—但也不得不考虑Dalvik dex的兼容性?

拿这种东西当知识不如直接说: SAMFunc/*void()*/ f=()->{} 编译期生成对 j.l.invoke.LambdaMetaFactory 静态同名方法(Lookup,String name,MethodType) 的调用,并把结果CallSite (仅首次计算)再次调用;这个“元工厂”把()->{}代码生成为 new T(){} 的具体class加载,返回它 static get(...localvars) 的调用句柄,于是得到目标接口的对象

为什么必须用dyn: 没有为什么,也不是因为参数类型不确定。反射交给目标SAM函的NameAndType 也能按照impl的参数(=field)动态定义/init this implements SAMFnType ,来创建o.impl()的代理。Android 最初就是反射直接Proxy class

当你写 class Out{int a; new T(){a} } ,相当于生成一个 Out$1 extends T ,它的构造器把(编译器确定共享序的)局部变量存在this,内部代码是有两个上下文的,()->{} 里也一样,只是这次 Out$1 的创建由运行时metafactory()负责,动态链接 T getLambda(int a); 完全是 Oracle 的私货,Kt 1.5 才支持这个 code size 优化

这些内容太细节,工程界自然完全不知道,所以说是什么“metafactory本身的签名不定” “创建调用的参数不确定” —的确不定,但重点是在T子类this.getClass() 定义不确定,这就涉及闭包,而今天的人都tm不知道{x+1}等函数是有词法上下文,由编译器统一保存位置的! 🥲

因为每个开发者眼前都只有自己的一亩三分地,编程是为效果
然而框架的开发者却不懂这个道理,想传教自己的设计有多“可扩展” 😒

我就好奇啊,中国也没几个人真做字节码框架啊,考的有意义吗? 觉得很厉害??🤔🙏

ref: https://jakewharton.com/d8-library-desugaring/ dex 上 stdlib-jre8 的 hashcode, notnull 测试都转换回老版本了,同时兼容Stream等新API