@data @override @pure @fun @overrideFun等我先把这些 kt4java Annotation 实现再说
@in @out @some
@get @set @protectedGet @protectedSet
duangsuse::Echo
@data @override @pure @fun @overrideFun @in @out @some @get @set @protectedGet @protectedSet 等我先把这些 kt4java Annotation 实现再说
├── [ 1.8K] Data.class
├── [ 1.1K] KvMap$1.class
├── [ 917] KvMap$KvPair$Mutable$Of.class
├── [ 505] KvMap$KvPair$Mutable.class
├── [ 2.0K] KvMap$KvPair$Of.class
├── [ 667] KvMap$KvPair.class
├── [ 2.5K] KvMap$Mutable.class
├── [ 4.5K] KvMap.class
├── [ 447] OperationNotSupportedException$Mutability.class
├── [ 1.1K] OperationNotSupportedException.class
├── [ 1.6K] RangeIE$IntRange.class
├── [ 1.6K] RangeIE$LongRange.class
├── [ 381] RangeIE.class
├── [ 1.7K] RangeMap$Mutable.class
├── [ 990] RangeMap$Ray$Shadow.class
├── [ 1.1K] RangeMap$Ray$White.class
├── [ 1.1K] RangeMap$Ray.class
├── [ 1.7K] RangeMap.class
└── [ 258] RayRangeMap.class
其实为什么不用 Kotlin,还要加上 annotation processing 也用 Java,这张看得很明白
Kotlin 必须有 Kotlin Runtime,那可大啊,而且小不了…… Scala 也一样
他们的通病……
有时候,我觉得如果利用 Annotation 扩展,Java 也不一定会怎么样差……
你们看了我上面的消息,肯定认为我恨死 Java 了,其实我真的不是恨 Java,我『恨』的是辣鸡编程风格。
我是函数式风格的程序员,看不惯一大堆 boilerplate 代码(何况我要对正在编写的东西实时进行模拟的,太多行代码会严重影响我的手感,太复杂的继承结构和数据封装、无必要的抽象更是会导致我像吃了 💩 一样难受)。
很多人不知道怎么写 Annotation Processor(不过知道怎么用),但是绝大部分人都会写 C Macro,我相信很多 Rust 的程序员也都会写和 regex 语法很像的
macro_rules!
对于我来说,代码生成、修改 AST 就是我的老本行…… 混饭吃的东西,当然这个不是不可说(其实我也是从周老的《JVM 最佳实践》抄的)怎么说呢,我先举一段 #Kotlin 代码,如果连这个都搞不懂就不要看了(当然也看不懂,因为你连括扑「排序」都不会,还需要学习啊),用我写出来的库吧。
sealed class SumTree {
data class Group(val children: Iterable<SumTree>): SumTree()
data class Tip(val n: Int): SumTree()
interface Visitor<out R> {
fun see(x: Tip):R fun see(g: Group):R }
fun <R> rewriteBy(vis: Visitor<R>): R = when (this) {
is Tip -> vis.see(this)
is Group -> vis.see(this)
}
}
object Sumer : SumTree.Visitor<Int> {
override fun see(x: SumTree.Tip) = x.n
override fun see(g: SumTree.Group) = g.children.sumBy { sum(it) }
private fun sum(t: SumTree) = t.rewriteBy(this)
}
>>> SumTree.Group(listOf(1,2,3).map(SumTree::Tip).asIterable())
Group(children=[Tip(n=1), Tip(n=2), Tip(n=3)])
>>> SumTree.Group(listOf(1,2,3).map(SumTree::Tip).asIterable()).rewriteBy(Sumer)
6
(之前我一直以为我有 see: Sumer.(Tip) -> R; Sumer.(Group) -> R 就万事大吉了,艹,还得 Visit fun 专门去 instance check(其实我也可以抽象出一个
Tip.plus(Tip): T,这样就能直接在一个群(group),而不是 List<Int> 什么的里执行计算也就是 Javac 的 Inline annotation processor JSR-269 API 啦,
javax.annotation.processing.AbstractProcessor
你要处理生成的,就是 javax.lang.model.element, Java 的 AST,可惜是不可改变的一段代码,我举个简单的例子,比如说给方法的名字加前缀:
(其实已经不简单了…… 比较长,而且实现的东西不多……
看来自动生成代码还是要更多努力才行的,不过我不是说 Javac 这样就理所应当。
骚一点的(类似 Python decorator 的手段,我把部分数据加在特定的
@interface 上,而不是 Annotation Processor 看的那种上面)simple-ap.zip
11.6 KB
🐴 的,Annotation Processor 那么花,居然只能用来「访问」部分数据,它不能改变源语法树!
@NamePrefixed(prefix = "Aa") public class A {}
[DuangSUSE@duangsuse]~/Projects/essay/simple-ap/out/production/kt4java% javac -cp . -processor Processor ../../../src/A.java -XprintRounds -XprintProcessorInfo循环 1:
输入文件: {A}
注释: [NamePrefixed]
最后一个循环: false
处理程序Processor与[NamePrefixed]匹配并返回true。
注: Prefix: Aa from [NamePrefixed]
警告: Name A does not started with Aa
循环 2:
输入文件: {}
注释: []
最后一个循环: true
1 个警告
请问,
原来它只能用来『访问』AST!而且还是『部分地』访问!
有再大帮助也没有暖用
duangsuse::Echo
为什么我感觉像是在吃 💩
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.util.ElementScanner6;
import static javax.tools.Diagnostic.Kind.*;
@SupportedAnnotationTypes("NamePrefixed")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class Processor extends BaseProcessor {
public Processor() {}🤔 #Java #Type #PLT 其实在 Java 内部也是存在 Union type (∪) 和 Intersection type (∩) 的
Union type (A∪B) 视为一个值的集合,它的值可能是 A 或 B 的一种
这种情况就是 Java 1.7 引入的 multi-catch
Intersection type (A∩B) 视为一个值的集合,它的值同时属于 A 和 B。
∩ 对称着逻辑运算符 logical and (∧),它的意思是 x∈A ∧ x∈B
Union type (A∪B) 视为一个值的集合,它的值可能是 A 或 B 的一种
这种情况就是 Java 1.7 引入的 multi-catch
(A∪B) = {x | x∈A ∨ x∈B}
(Int∪String) = {1,2,3,"A","B","C",...}
try { scanUserInput(); }
catch (EOFException | ReadError | ParserError e) { e.printStackTrace(); }
(Java 好慢啊!!!虽然 Kotlin 也没有明确的更多 Union 扩展)Intersection type (A∩B) 视为一个值的集合,它的值同时属于 A 和 B。
∩ 对称着逻辑运算符 logical and (∧),它的意思是 x∈A ∧ x∈B
(A∩B) = {x | x∈A ∧ x∈B}
(String∩Object) = {"a", "str", ...}
(Exception∩RuntimeException) = RuntimeException
这种情况就是 Java 一直以来不常见的语法:Type parameter with intersection (&) bound<T extends Comparable<T> & Preety> List<Pretty.Document> preetyOrdered(List<T> inputs);
duangsuse::Echo
import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.util.ElementScanner6; import static javax.tools.Diagnostic.Kind.*; @SupportedAnnotationTypes("NamePrefixed") @Support…
The apt reflective APIs provide a build-time, source-based, read-only view of program structure. (FUCK YOU!) 😭
char hexDigit(int b) { return (b >= 10)? 'a'+(b -10) : '0'+b; }This media is not supported in your browser
VIEW IN TELEGRAM
我可能要思考一下,毕竟我得专门为这类事情建立一个 Gradle task (plugin)... 来生成代码
虽然这不是不可行,可其实我只需要修改部分代码,而不是利用 APT 的接口加上 JavaPoet 完全自己生成代码(然后再通过 Javac 编译),那么就不得不使用类似字节码修改的方式来完成
emmm.... 代码生成不难,但是毕竟是第一次做,太麻烦了,不如暂时拉倒。
虽然这不是不可行,可其实我只需要修改部分代码,而不是利用 APT 的接口加上 JavaPoet 完全自己生成代码(然后再通过 Javac 编译),那么就不得不使用类似字节码修改的方式来完成
emmm.... 代码生成不难,但是毕竟是第一次做,太麻烦了,不如暂时拉倒。
Forwarded from dnaugsuz
Java 有个 Project lombok
可是我不敢相信它真的能做到写得好看,我非常需要 Kotlin 的
我想的是写成 Kotlin 那样
即便构造语法树结构有些冗长,但是如果有效也是好的,哪怕是字节码都行
可关键是目前这种只能上第三方 Task 的情况有点超出预期了,唉
可是我不敢相信它真的能做到写得好看,我非常需要 Kotlin 的
data, in, out, get, set 来简化代码(辣鸡 Java 不支持啊!)我想的是写成 Kotlin 那样
即便构造语法树结构有些冗长,但是如果有效也是好的,哪怕是字节码都行
可关键是目前这种只能上第三方 Task 的情况有点超出预期了,唉
dnaugsuz
基本满意,可是我真的很想给这些 Annotation 弄成 alllower 的…… 我讨厌无论实际语义如何都用『统一』的代码风格
居然连 @Builder 都有了,我瞬间有一种官方逼死同人的感觉,本来我也想写的…… 😿 看来意义降低一半了
Forwarded from dnaugsuz
是的,场景、能用是关键,所以代码质量不重要、抽提逻辑也不重要,个人有的强迫症也不重要,满足需求就足够了,任何可复用的库,都应该是满足需求的,这点很正确。
只有最终实现了才行,实现最重要,所以才有你看到那么多发布了 11 个版本还是一样复杂的库,真的是它们已经无从简化?其实是它们已经满足了需求和自己的场景,站稳了自己的位置,当然不需要『最大化』某些东西了。作者看得懂就够了。
当然不需要再写第二遍了,当然不需要 Literate Programming 了,因为他们能用了嘛、IP(Intelligence Property)。
是的,第二遍不重要,只有傻逼才会写第二遍、只有傻逼才会慢慢按层次把一个个
Java 也是这样的,不过 Kotlin 不是这样的,我甚至没听说过 Kotlin 可以把
实现了就好嘛,也不需要再三思考自己到底写的是为了什么、不需要再三重新审视自己的需求、数据的依赖、生存周期、可变性、最终的目标算法、编程技巧,到底最终的『需求』是什么,最后写出一大堆所谓的设计模式出来,然后不是设计模式的都写得像脚本或者汇编、Shell 一般,其实到底最终还是没有抓住关键问题在哪里。
我是个很垃圾的工程师,也见过很牛逼的工程师写的程序,他们很厉害,他们重视面向对象设计模式、可复用、可组合(比如,XMLPull 的 Data Event 模式)、可配置(比方说,一切皆 XML)、测试、部署、DevOps,甚至还有一群人成天把什么『Presentation』『case study』挂在嘴边 — 就是那些所谓的『Agile Development』者啦。
是,这个问题对我重要,因为我其实也觉得 class file 的体积,那一点代码和 term 不算什么,只是我之前老毛病犯了,忍不住想把最终代码弄小点,我明白它没有意义,但被『所有人』忽视的点里,难道就没有应该重视的东西?难道『堆砌』代码而不是做『设计』的只有我一个?
当然,不少人不仅仅不会觉得输出 java class 代码的体积算得了什么,慢慢地连自己编写给自己看的代码体积也不算什么了,你看 — 它不是满足需求了么?它通过测试了不是么?它在 JDK 里面了不是么?
回到我们的初心,后来才发现,其实『写好代码』和『做完工作』,它们根本不冲突。
并且,好的程序员就应该把『写好代码』作为自己的最低标准。
只有最终实现了才行,实现最重要,所以才有你看到那么多发布了 11 个版本还是一样复杂的库,真的是它们已经无从简化?其实是它们已经满足了需求和自己的场景,站稳了自己的位置,当然不需要『最大化』某些东西了。作者看得懂就够了。
当然不需要再写第二遍了,当然不需要 Literate Programming 了,因为他们能用了嘛、IP(Intelligence Property)。
是的,第二遍不重要,只有傻逼才会写第二遍、只有傻逼才会慢慢按层次把一个个
interface 划分开来、只有傻逼才会对一些诸如最终 .class 体积之类的问题死缠烂打(其实我也没死揪),大家都是那样的,把 isEmpty, containsKey, containsValue, entrySet 什么的一股脑塞到某一个地方去,脑容量大一点的人不再乎。Java 也是这样的,不过 Kotlin 不是这样的,我甚至没听说过 Kotlin 可以把
interface 里放上 default 实现。实现了就好嘛,也不需要再三思考自己到底写的是为了什么、不需要再三重新审视自己的需求、数据的依赖、生存周期、可变性、最终的目标算法、编程技巧,到底最终的『需求』是什么,最后写出一大堆所谓的设计模式出来,然后不是设计模式的都写得像脚本或者汇编、Shell 一般,其实到底最终还是没有抓住关键问题在哪里。
我是个很垃圾的工程师,也见过很牛逼的工程师写的程序,他们很厉害,他们重视面向对象设计模式、可复用、可组合(比如,XMLPull 的 Data Event 模式)、可配置(比方说,一切皆 XML)、测试、部署、DevOps,甚至还有一群人成天把什么『Presentation』『case study』挂在嘴边 — 就是那些所谓的『Agile Development』者啦。
是,这个问题对我重要,因为我其实也觉得 class file 的体积,那一点代码和 term 不算什么,只是我之前老毛病犯了,忍不住想把最终代码弄小点,我明白它没有意义,但被『所有人』忽视的点里,难道就没有应该重视的东西?难道『堆砌』代码而不是做『设计』的只有我一个?
当然,不少人不仅仅不会觉得输出 java class 代码的体积算得了什么,慢慢地连自己编写给自己看的代码体积也不算什么了,你看 — 它不是满足需求了么?它通过测试了不是么?它在 JDK 里面了不是么?
回到我们的初心,后来才发现,其实『写好代码』和『做完工作』,它们根本不冲突。
并且,好的程序员就应该把『写好代码』作为自己的最低标准。
酷 壳 - CoolShell
再谈敏捷和ThoughtWorks中国咨询师 | 酷 壳 - CoolShell
duangsuse::Echo
『讷,这就是核心部分的代码了,没有问题我去睡觉了』
哦,对了,不得不说一下
其实 RangeMap 本来应该是
RangeMap 是一个在有序的序列存储后端上执行二分查找的算法,所以可以达到这样的时间/空间复杂度
(其实本身不是特别复杂,不过作为一个类封装起来会好用很多)
RangeMap 到底是怎么工作的其实 RangeMap 本来应该是
template<> RangeMap<int, int>, 反正它的真名是 RayRangeMap,利用数学的『射线』进行建模优化的算法,也就利用 Comparable 泛化了一下……val rm = RangeMap<Int, String>()理论上,RangeMap 只需要
rm[0 until 1] = "0"
rm[1 until 4] = "123"
rm[4 until 6] = "45"
assert (0 in rm)
assert (3 in rm)
assert (rm.containsEdge(0))
assert (6 !in rm)
assert (rm[0] == "0")
assert (rm[3] == "123")
assert (rm[5] == "45")
assert (rm.containsShadow(5))
O(log2 n) 的复杂度即可查找到某一个项目,并且比 HashMap 节省空间。RangeMap 是一个在有序的序列存储后端上执行二分查找的算法,所以可以达到这样的时间/空间复杂度
(其实本身不是特别复杂,不过作为一个类封装起来会好用很多)