关于在 Java 里面实现命名参数的一些想法 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
netabare
V2EX    程序员

关于在 Java 里面实现命名参数的一些想法

  •  
  •   netabare 102 天前 2638 次点击
    这是一个创建于 102 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近看到了一篇文章,Approximating Named Arguments in Java,讲的是怎么在 Java 里面模拟类似 Python 的命名参数,整体来说不算太出乎我的意料吧,主要还是原地改状态和 builder 模式,还有个 with 的写法虽然看起来有点意思,但总感觉缺点什么。

    不过看到这个风格的写法的时候,突然想起了之前自己写的一个项目里面用 lambda 来模拟模式匹配的写法。

    kMeans(X, nClusters, opts -> opts.maxIter = 1000); 
    String guess = new Match<Shape, String>(new Circle()) .is(Circle.class, (circle) -> "我是一个圆") .is(Square.class, (square) -> "我有点方") .otherwise((Shape shape) -> "你看不见我") .get(); 

    然后突然想,是不是可以用一个 lambda 参数来当占位符,来变相实现命名参数。大概是(maxIter -> 1000)这样的感觉。

    但仔细想的话,Java 里面没有 currying 和 uncurrying 的概念,换句话说,本来我构想的在 Java 里应该用的是 Supplier 接口,但现在我手头上的是 Function<T, U>而且其中的 T 无法被忽略掉。似乎是一个很致命的问题。

    看起来就卡死了……

    然后想了想,其实我需要的是一个能把 Function<T, U>消掉的东西,回忆了一下最近 code review 从隔壁 senior 学来的 static class 的一些奇怪的写法,然后又想了想自己最近在搞的 Ref<T>,然后折腾出了一坨这玩意:

    static final class KMeans { static final class Options { int maxIter = 300; double tol = 1e-4; boolean verbose = false; // ... } @FunctionalInterface interface Param { void apply(Options o); } static Param maxIter(IntSupplier s) { return o -> o.maxIter = s.getAsInt(); } static Param tol(DoubleSupplier s) { return o -> o.tol = s.getAsDouble(); } static Param verbose(BooleanSupplier s) { return o -> o.verbose = s.getAsBoolean(); } static Output kMeans(Data X, int nClusters, int maxIter, boolean verbose, double tol, ...) { ... } // 这里是具体的代码逻辑 static Output kMeans(Data X, int nClusters, Param... params) { var optiOns= new Options(); Arrays.stream(params).forEach(applier -> applier.apply(options)); // 然后就可以继续实现具体的代码逻辑了 } } 

    psvm 里面可能长这样:

    var X = new Data(...); var res1 = KMeans.kMeans(X, 3, 1000, 1e-8, ...); // 常见的写法 var res2 = KMeans.kMeans(X, 3, KMeans.verbose(() -> true), KMeans.tol(() -> 1e-6), KMeans.maxIter(() -> 1000), ...); // 现在可以这么写了,而且顺序也可以换来换去 // 如果不用担心 import 会变得乱七八糟的话还可以这样 var res3 = kMeans(X, 3, maxIter(() -> 1000), tol(() -> 1e-6), verbose(() -> true), ...); 

    感觉看起来有点像 Python 的命名语法,也不至于像 builder 模式那么嗦了。

    但是总感觉好像还是有点嗦(

    然后还可以稍微再改改。

    interface Constant { static Param maxIter(int maxIter) { return o -> o.maxIter = maxIter; } static Param tol(double tol) { return o -> o.tol = tol; } } // 用法 IntSupplier someOtherCalc = () -> { return ... ; } // 假设它会返回 2048 var res4 = kMeans(X, 3, maxIter(someOtherCalc) Constant.tol(1e-6), Constant.verbose(true), ...); 

    但似乎显得更怪异了,而且这么写代码感觉会被人打(

    8 条回复    2025-08-16 12:46:19 +08:00
    yazinnnn0
        1
    yazinnnn0  
       102 天前
    为啥不用 kotlin 或者 scala
    xtreme1
        2
    xtreme1  
       102 天前
    vavr 里有类似的东西
    用多了在 idea 中编辑时巨卡
    chendy
        3
    chendy  
       102 天前
    一个劣化版本

    public static void main(String[] args) {
    Demo student = new Demo(
    d -> d.setId(1L),
    d -> d.setAge(24),
    d -> d.setName("Student")
    );
    }

    @Data
    public static class Demo {
    private long id;
    private String name;
    private int age;

    public Demo() {
    }

    public Demo(Consumer<Demo>... consumers) {
    for (Consumer<Demo> consumer : consumers) {
    consumer.accept(this);
    }
    }
    }
    guyeu
        4
    guyeu  
       102 天前
    Java 没有默认参数、关键字参数这些功能,对命名参数也没有啥需求啊。。
    Ketteiron
        5
    Ketteiron  
       101 天前
    语言的语法是用来增强表达的能力,你这个设计不太好,增加了阅读/编写的复杂度,但是收益无法抵消额外开销,在我看来是负优化,想在 java 里搞奇技淫巧的应该直接转 kotlin/scala ,别指望能在垃圾语言上屎上雕花,只能屎上雕屎。
    命名参数这玩意,是为了把参数从水平转成垂直,就像浏览器的普通标签页 vs 垂直标签页,语法上想实现这种功能,一定要支持默认参数/可选参数,所以 java 只能洗洗睡了。
    而且命名参数也不是最好的设计,js 不支持命名参数,但是 es6 的参数解构远比命名参数好用。
    cloudzhou
        6
    cloudzhou  
       101 天前
    在业务开发方面,脚本语言几乎式微,一个原因就是维护性不好,写的开心维护的累
    Perl 更是誉为 readonly 语言
    So ,builder 已经足够可以了

    你这个模式是 Golang 的 Option 模式,用于某些选项类,不是通用参数
    netabare
        7
    netabare  
    OP
       101 天前
    @yazinnnn0
    @dssxzuxc 我自己写肯定直接选择 Dotty 或者 ML 了,只是看到有人讨论「怎么在 Java 里面引入命名参数」这个帖子,然后产生了一点脑洞而已。而且 @dssxzuxc 说的「屎上雕屎」这点我也很认同,我自己对命名参数这玩意本身也没很感冒。

    @chendy
    其实你这个想法感觉有点像「依赖注入」或者一个「劣化版本的 Scope 」了吧,我个人倒是感觉其实语法上和 builder 模式算是等价的?
    xuanbg
        8
    xuanbg  
       100 天前
    好玩,但没什么用
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4677 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 04:01 PVG 12:01 LAX 20:01 JFK 23:01
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86