V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
so2back
V2EX  ›  Java

一个 Java 的问题,来这里问下,这问题很白痴吗。。怎么我在 segmentfault 提问被踩了两次,又没一个人肯回答一下。。

  •  
  •   so2back · 2019-11-30 09:59:47 +08:00 · 10142 次点击
    这是一个创建于 1849 天前的主题,其中的信息可能已经有所发展或是发生改变。

    好像不能发图片,我直接复制一下自己用来解释的代码,应该能看得懂说什么吧。。。我就想问问 generateMap 这个方法是能够实现的了的吗。。


    public static void main(String[] args) { String name = "abc"; Integer age = 20; List<string> hobby = new ArrayList(); hobby.add("run"); hobby.add("swim"); // 这里直接调用方法,放入想要生成 map 的参数 generateMap(name, age, hobby); }</string>

    public static Map generateMap(Object... entrys) {
        // 然后在这里根据接到的可变参数,生成一个 map,生成的 map 如下,就是根据收到的参数的名称对应放进去
    

    // Map map = new HashMap(); // map.put("name", name); // map.put("name", age); // map.put("name", hobby); }

    第 1 条附言  ·  2019-11-30 11:03:08 +08:00
    ```java
    public static void main(String[] args) {
    String name = "abc";
    Integer age = 20;
    List<String> hobby = new ArrayList();
    hobby.add("run");
    hobby.add("swim");
    // 这里直接调用方法,放入想要生成 map 的参数
    generateMap(name, age, hobby);
    }

    public static Map generateMap(Object... entrys) {
    // 然后在这里根据接到的可变参数,生成一个 map,生成的 map 如下,就是根据收到的参数的名称对应放进去
    // Map map = new HashMap();
    // map.put("name", name);
    // map.put("age", age);
    // map.put("hobby", hobby);
    }
    ```
    86 条回复    2019-12-02 18:52:11 +08:00
    so2back
        1
    so2back  
    OP
       2019-11-30 10:00:45 +08:00
    v2ex 怎么发代码。。格式全乱了。。
    so2back
        2
    so2back  
    OP
       2019-11-30 10:01:22 +08:00
    public static void main(String[] args) {
    String name = "abc";
    Integer age = 20;
    List<String> hobby = new ArrayList();
    hobby.add("run");
    hobby.add("swim");
    // 这里直接调用方法,放入想要生成 map 的参数
    generateMap(name, age, hobby);
    }

    public static Map generateMap(Object... entrys) {
    // 然后在这里根据接到的可变参数,生成一个 map,生成的 map 如下,就是根据收到的参数的名称对应放进去
    // Map map = new HashMap();
    // map.put("name", name);
    // map.put("name", age);
    // map.put("name", hobby);
    }
    stancaohua
        3
    stancaohua  
       2019-11-30 10:05:34 +08:00 via Android
    不行,去学下 map 怎么用的吧
    sumarker
        4
    sumarker  
       2019-11-30 10:08:17 +08:00
    你的 map 的 key 都一样啊。。。
    apach
        5
    apach  
       2019-11-30 10:09:41 +08:00 via iPhone
    可以
    vjnjc
        6
    vjnjc  
       2019-11-30 10:09:43 +08:00
    被踩是因为你没有描述清楚问题。请组织下语言用中文描述一遍。
    你这样肯定不行,map.get("name")会得到 hobby
    manami
        7
    manami  
       2019-11-30 10:10:18 +08:00
    map 的 key 不可以重复
    so2back
        8
    so2back  
    OP
       2019-11-30 10:10:20 +08:00
    @stancaohua map 我会用啊,只是觉得可能不在我知识了解范围内,我不知道可不可以实现这样的
    so2back
        9
    so2back  
    OP
       2019-11-30 10:10:38 +08:00
    @sumarker 这就是我要达到的效果啊
    so2back
        10
    so2back  
    OP
       2019-11-30 10:11:35 +08:00
    好吧,各位,我写错代码了。。。直接 ctrl + D 复制的没改过来。。下面才是
    public static void main(String[] args) {
    String name = "abc";
    Integer age = 20;
    List<String> hobby = new ArrayList();
    hobby.add("run");
    hobby.add("swim");
    // 这里直接调用方法,放入想要生成 map 的参数
    generateMap(name, age, hobby);
    }

    public static Map generateMap(Object... entrys) {
    // 然后在这里根据接到的可变参数,生成一个 map,生成的 map 如下,就是根据收到的参数的名称对应放进去
    // Map map = new HashMap();
    // map.put("name", name);
    // map.put("age", age);
    // map.put("hobby", hobby);
    }
    wysnylc
        11
    wysnylc  
       2019-11-30 10:12:16 +08:00
    java9 有 map.of
    还有 beanutil https://blog.csdn.net/langqiao123/article/details/72961383
    不要想些有的没的,好好百度好好学习,你想弄的骚操作早就有千万个前人想过了
    lnchy
        12
    lnchy  
       2019-11-30 10:12:21 +08:00
    map 有问题,然后就是形参传给实参的时候 java 会重新申请个指针,没法拿到名称
    so2back
        13
    so2back  
    OP
       2019-11-30 10:12:34 +08:00
    @manami ctrl D 直接复制的代码忘改了#(苦笑)
    lbfeng
        14
    lbfeng  
       2019-11-30 10:12:45 +08:00
    先学下 markdown 吧
    ```java
    代码在中间
    ```
    maninfog
        15
    maninfog  
       2019-11-30 10:12:48 +08:00 via iPhone
    行不行你自己编译器敲下不就得了
    so2back
        16
    so2back  
    OP
       2019-11-30 10:13:42 +08:00
    @wysnylc 其实是我项目这类冗余代码有点多,我想直接用个方法整合一下- -
    so2back
        17
    so2back  
    OP
       2019-11-30 10:13:55 +08:00
    @lbfeng 好的
    cmdOptionKana
        18
    cmdOptionKana  
       2019-11-30 10:18:18 +08:00   ❤️ 4
    问题描述不够清晰,建议明显地分为三个部分来描述问题

    1. 我想得到什么
    2. 我尝试了怎么做
    3. 我得到了怎样的错误结果
    sumarker
        19
    sumarker  
       2019-11-30 10:18:52 +08:00
    @so2back
    你这这个生成 map 都方法怎么把可变参数变成具体都参数的?
    可变参数的方法在接收你的入参以后 参数就变 Object []entrys 了啊,
    你还有取数组 分别复制对应的参数吗?
    反正我是没看懂你要干嘛。。。
    Raymon111111
        20
    Raymon111111  
       2019-11-30 10:19:11 +08:00   ❤️ 1
    你说的参数名称是 name, age, hobby 这种变量名?

    map 想放的东西是 map.put("name", name); map.put("age", age) 这样?

    这个大概率是做不到的, 对于编译器来说, 人写的这些的变量名只是类似占位符的作用, 打包变成 .class 文件之后, 如果不特地开启保留变量名这种功能, 类方法所带的参数变成能在 .class 里找到的只有局部变量的槽位, 第一个参数(非 static 就是 this)在 slot 1, 第二个参数在 slot2 等等这样, 而不会有程序中真正的名字.

    至于用 IDE debug 的时候为什么又能看见真正的名字, 可以搜一下 LVT.
    hoyixi
        21
    hoyixi  
       2019-11-30 10:20:02 +08:00
    还是没明白你到底想问什么

    你直接放 segmentfault 原问题的链接不就行了
    wysnylc
        22
    wysnylc  
       2019-11-30 10:20:38 +08:00
    @lnchy #12 如果重新申请了指针,请问怎么做到对象引用的传递?
    @so2back #16 java8 以前方法内无法拿到参数的参数名只能拿类型,java8 后有 Method.getParameters()方法可以获取
    petelin
        23
    petelin  
       2019-11-30 10:21:29 +08:00 via iPhone
    j 这个问题本质是能不能拿到变量的名字
    这得靠反射 我觉得应该不行
    不过你可以把它们封装到一个类里 bean 是可以变成 map 的
    oneisall8955
        24
    oneisall8955  
       2019-11-30 10:22:53 +08:00 via Android
    JAVA 中估计不行,一个是变量名称一个是变量值。不过 js 中的 es6 版本好像有语法支持生成这样的 map 对象
    chenshun00
        25
    chenshun00  
       2019-11-30 10:33:55 +08:00
    1、我要做什么,要达到的目的是什么
    2、我做的过程出了 xxx 问题
    3、针对 xxx 问题我 google 到了一下链接,但是并没有解决我的问题
    xxx 链接 1...2....3
    hantsy
        26
    hantsy  
       2019-11-30 10:43:23 +08:00
    Map.of("name", name, "age", age)
    hantsy
        27
    hantsy  
       2019-11-30 10:44:13 +08:00
    Map.ofEntries(entry("name", name), entry("age", age), ....)
    msg7086
        28
    msg7086  
       2019-11-30 10:46:11 +08:00   ❤️ 1
    那 generateMap("bob", 24, new ArrayList()); 会发生什么?

    被踩的原因是这么简单的动脑筋都没有动。

    > 这问题很白痴吗

    这问题不白痴。但是你没有说明你的思考过程,让人觉得你就是个只会抛问题而不会思考的人。
    请花半个小时阅读以下《提问的智慧》,免得以后再被人踩。
    so2back
        29
    so2back  
    OP
       2019-11-30 10:53:20 +08:00
    so2back
        30
    so2back  
    OP
       2019-11-30 10:59:20 +08:00
    @msg7086 你是不是没看明白我说的什么?我自己内部用的方法干嘛这么折腾自己?还有,我上面已经解释了这代码有问题了,大概就是被踩的原因了,麻烦看看好吧,还有,你说的这么简单是咋回事?这么简单你又不说是什么,非要来杠我一下?我承认我没说我的思考过程,这里不能发图片,不然我发我搜索这个问题的所有网页记录给你看?
    so2back
        31
    so2back  
    OP
       2019-11-30 10:59:38 +08:00
    @hantsy get,回头试试,今天加班- -
    leafmaple
        32
    leafmaple  
       2019-11-30 10:59:53 +08:00   ❤️ 2
    @msg7086 #28 手抖发成感谢了。想起来最近看的一篇文章,有的时候提问的人就是想问一个简单的问题,就是因为没找到解决方案,才来求助,向你们这些动不动就拉出提问的智慧,让想起来一句经典的话,Read The Fucking Manual,遇到问题了才会来提问,而不是想得到冷冰冰的去看提问的智慧,网络不仅没有拉近人群的交流,反而更加生疏了吧。
    个人观点,不喜勿喷
    so2back
        33
    so2back  
    OP
       2019-11-30 11:01:56 +08:00
    @petelin 恩,本质确实是能不能拿到变量的名称,但是还要鉴于可变参数,反射不知道搞不搞得了,不过弄成 bean 不行吧,可变参数挡着了
    cedoo22
        34
    cedoo22  
       2019-11-30 11:21:38 +08:00   ❤️ 1
    java 5 语言文档:
    Enhancements in JDK 5:
    ....
    Varargs - This facility eliminates the need for manually boxing up argument lists into an array when invoking methods that accept variable-length argument lists.

    这个和自动装箱一样,把可变长的 list 自动装箱成了一个数组,所以数组的某个位置上的成员是没有‘别名’的。
    试着写了一个 T.java,编译报错:

    T.java:3: 错误: 无法在 T 中同时声明 m(String...)和 m(String[])
    msg7086
        35
    msg7086  
       2019-11-30 11:24:36 +08:00   ❤️ 9
    我的确没有看明白你说的是什么。我相信如果你的问题描述得很明白,也不需要下面十几号人一个一个去猜你的问题了,也根本不需要我来尝试帮你解决问题,早就有人答到你的点子上了。

    不过根据你后面的回复来看,我发现我的回答一点都没有错,完全符合你说的问题。你需要获得的是调用者向被调用者发送的参数的名称。我们都知道变量名只是一个给人看的标识符,编译成代码以后就变成了具体的值(至于是传值还是传引用这个暂且不表)。age = 24; generateMap(age); 编译完后就成了 generateMap(24) 了。对于 generateMap 函数来说,怎么通过 24 拿到 age ?

    如果你一定要这么做的话,当然可以用反射去试。拿到对象的地址,然后去完整的变量列表里搜。只要你能保证每个传入的数据不存在两个变量里,那么用变量传的话是有可能反过来找到曾经用过的变量名的。另外因为是对象的地址,所以非对象的数据是找不到的(比如说数字立即量)。至于在 Java 里,能不能存下你写代码时的变量名,能不能列出所有的本地变量的地址,这我就不得而知了。

    我前面告诉你的是你的问题为什么会被踩。如果你一定要理解为这是我的观点,那是你的事情。我本人对你原本就没有负面或者正面的评价。你要是觉得我在杠那也是你的事情。我也没有一丁点的兴趣关心你是否有思考过程,我也不像 SF 那里的人那样见到你的问题就是一顿踩,当然你怎么看我的那也是你的事情。这个帖子最大的问题就是没把问题说清楚,上来就是一句「应该能看得懂说什么吧」,意思是你觉得你不用解释太多了,坛友理所当然就应该看得懂你的问题,看不懂当然是他们的问题。看看这二三十层楼都交换了多少无用信息吧。

    言尽于此,不再打扰。
    msg7086
        36
    msg7086  
       2019-11-30 11:30:05 +08:00   ❤️ 4
    @leafmaple 我不会对「简单的问题」说提问的智慧。
    当我说提问的智慧的时候,多数是因为提问者没有把问题描述清楚,或者加入了各种错误的前提假设,而这些恰恰是提问的智慧中提到的最重要的问题。同理我还会留言 XY 问题,这是当提问者想要 X 却来问 X 的某个不恰当解决方案 Y 的时候。
    即使是简单的问题我也会尽可能详细地回答,但这是一个模糊不清的问题,而不是一个简单的问题。
    更何况,这个问题并不简单。要实现这些要求,都已经可以做一个完整的类库了。
    dallaslu
        37
    dallaslu  
       2019-11-30 11:47:23 +08:00
    楼主挨喷并不冤枉。问题描述不清,基础知识不牢,编程思路不活。

    如楼上各位所说,方法内是形参,怎么可能直接取到实参的变量名呢?可变参数也是一个数组,数组内元素曾经用过的变量名更是不可考。(不过说 class 文件中找不到变量名的说法,并不完全认同。毕竟在有 java doc 的情况,注释都是能取得到的。但是那么多变量,怎么确定变量对应哪个名字又是另外一个难关了。)

    假设有这么一个超级 Bean,提供了所有可能的方法名,也是需要一个一个的 setName/setAge/setHobby 去调用的。一句 map.put(String, Object) 并不算冗余。

    用带 key 和 value 属性的 Entry 做参,也有点脱裤子放屁的嫌疑。不如试试 Bean 转 Map 的工具,或代码生成器吧。
    leafmaple
        38
    leafmaple  
       2019-11-30 11:53:50 +08:00   ❤️ 1
    @msg7086 #36 看了你在 35 的评论,其实我想表达的大概就是这个意思,可以指明那块做的还有问题,而不是直接让去读提问的智慧,因为有些人缺的可能就是一个点子,提问的智慧可以作为参考,让他再此之后的提问能有个规范。谢谢回复。交流难免有碰撞。
    loqixh
        39
    loqixh  
       2019-11-30 12:03:07 +08:00
    java c++ c#都做不到你想要的效果, 部分元编程强大的语言可以, 比如 D 语言
    so2back
        40
    so2back  
    OP
       2019-11-30 12:24:35 +08:00
    发一下自己好歹是思考过的可以把,我本意是觉得不需要这么麻烦的,我的问题可能真的描述不清楚,毕竟我的问题一开始就是写错了的,但是,你觉得这问题你看不下去,或者真的让你觉得烦的,能不能别就杠起我来了?身为一个程序猿,有问题自己先搞,实在不行才到网上问,你就一定得让我大篇幅的贴我究竟干了什么,才有资格问你了?老实说,你回答我了,我内心表示感谢,即使我没有明着回复你感谢你,但是你的解决方案我闲下来了肯定会试一试,你觉得我太水的你可以不管,别耽误您的时间可以把
    而且我问这个问题,我就想知道能不能实现,可能 jvm 原理我不了解那么多,那我来求请教一下,大家觉得问题有意义的可以讨论讨论,没必要参杂那么多没意义的内容
    今天加班,午休 up
    so2back
        41
    so2back  
    OP
       2019-11-30 12:26:07 +08:00
    靠,图片我这个是新号发不出去。。
    so2back
        42
    so2back  
    OP
       2019-11-30 12:28:32 +08:00
    上传到 imgur 了,后缀是
    DZVhUTT
    XffaLiS
    4HiWMBk
    so2back
        43
    so2back  
    OP
       2019-11-30 12:28:45 +08:00
    我还真是煞费苦心啊- -
    GrayXu
        44
    GrayXu  
       2019-11-30 12:48:15 +08:00
    不可以获得名字吧,这需求为啥不直接用 bean 呢…
    sutra
        45
    sutra  
       2019-11-30 12:58:24 +08:00 via iPhone
    我没能看懂你的问题。
    beidounanxizi
        46
    beidounanxizi  
       2019-11-30 13:03:10 +08:00
    被喷说明你又学到了 骚年 加油哟
    by73
        47
    by73  
       2019-11-30 13:11:04 +08:00
    看了半天,是希望获得函数实参的名称? emm 似乎不行,Reflection API 能得到变量名称的似乎只有 `Field` 和 `Parameter`,而且 Parameter 的名称多数都会被编译器优化掉,而而且 `Object... vars` 这个是当作一个 Parameter,而而而且 Java 传参是值传递,完全无法从形参得知实参的元信息。。
    Leigg
        48
    Leigg  
       2019-11-30 13:12:51 +08:00 via Android
    To learn how to ask a question.
    neoblackxt
        49
    neoblackxt  
       2019-11-30 13:18:09 +08:00   ❤️ 1
    我看懂了,你要的是 getOriginalArgName(Arg arg...)
    真是脑洞太开,我不会
    leaves615
        50
    leaves615  
       2019-11-30 13:59:08 +08:00
    想法非常的好,编程语言基础不够。把源码环境思维与编译后运行环境思维混在一起思考。
    donglingyongadls
        51
    donglingyongadls  
       2019-11-30 14:19:19 +08:00   ❤️ 2
    打了一堆字都没说明白,49 楼一句 getOriginalArgName(Arg arg...) 概括完了。
    获取字面量毫无意义,如果你已经知道了字面量了,你打字的时候为什么不存到一个变量里面去直接带过来?
    如果你不知道字面量,说明这个东西是运行时的一个变量,你为什么不直接把这个变量丢过来?

    处于想的太多又看的太少的状态
    louislivi
        52
    louislivi  
       2019-11-30 14:25:24 +08:00
    楼主想要的应该是动态传参,接受参数 利用参数名当 key 参数值当 value 自动生成 map 类型 对吧?
    louislivi
        53
    louislivi  
       2019-11-30 14:33:39 +08:00   ❤️ 1
    最终在函数中接收到的只是一个 Object[] ,貌似无法获取原变量名
    ace12
        54
    ace12  
       2019-11-30 14:49:29 +08:00
    这大概就是 java 需要 pojo 存储数据的原因吧
    jason19659
        55
    jason19659  
       2019-11-30 15:01:24 +08:00
    @neoblackxt #43 楼主说半天我没看懂。。。你这一句我就懂了。。
    Aresxue
        56
    Aresxue  
       2019-11-30 15:02:45 +08:00
    问题在于引用就只是个引用,编译过后就丢失了,随便找个源码看就知道了。
    Aresxue
        57
    Aresxue  
       2019-11-30 15:06:06 +08:00   ❤️ 1
    再贴一个 R 大的回答 java 能否获取到引用的名称? - 知乎
    https://www.zhihu.com/question/29643012 看完你就知道了
    Ky1inZhang
        58
    Ky1inZhang  
       2019-11-30 15:22:07 +08:00
    我理解你的问题是,如何获取参数名称。

    我也没仔细看,甩给你瞅瞅是不是这方向。

    反射获取参数名称
    https://blog.csdn.net/revitalizing/article/details/71036970

    函数获取参数名称的几种方法
    https://blog.csdn.net/wwwwenl/article/details/53427039
    so2back
        59
    so2back  
    OP
       2019-11-30 15:23:36 +08:00
    @beidounanxizi 还是有点难受,我有些想当然了- -
    so2back
        60
    so2back  
    OP
       2019-11-30 15:24:57 +08:00
    楼上的大部分我认真看了,不一一回复了,以后提问我会注意的,我以为直接贴代码可能可以理解问题,我想岔了
    cyrbuzz
        61
    cyrbuzz  
       2019-11-30 19:31:43 +08:00
    只有我一个人疑惑复制为啥不是 ctrl+C 而是+D 吗= =。
    340244120w
        62
    340244120w  
       2019-11-30 20:37:35 +08:00 via iPhone   ❤️ 1
    编译的时候加上-parameters 这样就能通过反射获取到方法名了。SpringMvc controller 方法参数绑定就这样获取的
    neoblackxt
        63
    neoblackxt  
       2019-11-30 21:03:21 +08:00
    @cyrbuzz 有一种 IDE 叫 intellij IDEA
    cyrbuzz
        64
    cyrbuzz  
       2019-11-30 22:34:00 +08:00
    @neoblackxt 我以为大部分都是 ctrl+d 选择同名所选单词= =。
    nowto
        65
    nowto  
       2019-11-30 23:08:31 +08:00
    只看代码。
    我们知道,变量都是有作用域的,name、age、hobby 作为 main 方法的局部变量,只限于在 main 方法中使用。
    而 generateMap 方法中并没有定义名字为 name、age、hobby 的局部变量,同时也没有成员变量是叫这些名字的,所以编译不会通过的。
    ClericPy
        66
    ClericPy  
       2019-11-30 23:13:18 +08:00
    该说的楼上们都说了...

    想起我当初第一次注册 stackoverflow 的时候... 第一个问题就被好几个人 -1, 从那以后再也没问过问题, 有任何情况, 自己转英文描述 google 之文档之, 也算长了记性

    努力吧
    youxiachai
        67
    youxiachai  
       2019-12-01 00:33:11 +08:00
    @neoblackxt 大佬牛逼。。。。。
    lz 应该多学学这大佬。。这说得多明白。。
    omysho
        68
    omysho  
       2019-12-01 00:36:28 +08:00 via Android
    @neoblackxt
    学习了,用了这么多年 JB 家的 IDE,就只会用 ideaVim 复制粘贴
    youxiachai
        69
    youxiachai  
       2019-12-01 00:37:11 +08:00
    其实,我能体会到 lz 的纠结,
    当我 5 年前还在读大学的时候,也想过类似的,拿原始参数名的玩意,做代码生成。。后来想想,真是都野鸡大学耽误事情,这个玩意懂一点编译原理都不会犯。。

    在 java 里头,lz 这个需求其实可以走 APT 的路子。。。

    而不是土法炼钢。。。
    Antihank
        70
    Antihank  
       2019-12-01 13:51:02 +08:00
    不要气馁,虽然问题很幼稚但是你的想法是很好的,继续进步!
    ooops
        71
    ooops  
       2019-12-01 13:57:44 +08:00
    楼主问不明白还不自知,你没问题会有这么多人说你提问有问题么?好好审视一下自己,b 了再见。
    FrankHB
        72
    FrankHB  
       2019-12-01 17:09:36 +08:00
    @youxiachai 你要往编译原理上靠那也是野鸡知识。
    这个(最表面上层次上的)问题说穿了很简单:形式参数作为绑定变量的名称,它对语言的语义无关紧要,正经的语言设计一开始就没有把它设计为可以让用户可靠地进行依赖的接口特性,而只是实现细节。
    更进一步的原理是,支持反射形式参数名破坏 alpha renaming 保证的语义等价性——保证重写规则中仅仅替换形式参数名,只要不冲突,就能保持语言的语义不变。
    这背后的原则是变量名的含义应对确定如何计算的语义规则不透明,语言设计原则上不需要干涉用户怎么命名变量,这样用户可以按需自己选择命名约定以满足不同的需求。(这也是一票重构工具的基础。)
    这在工程上给语言的设计者也带来了很大的方便——至少不需要就自然语言的含义纠结了,也不需要非得把命名规则这样零碎的东西写到语言的规格描述里才能用,就算写进去了也原则上不需要和改动语言特性冲突。
    (至于某些 PL 强迫用户使用 camelCase 之类的瓦釜雷鸣,说白了这来自特定自然语言内部的缺陷,跟 PL 的设计原则上本应无关——比如要是一开始用中文就连纠结 case 的基础都没有了——虽然中文有另外的破事。如何纵容自然语言的旮旯污染到 PL 设计上这就是另一回事了。)
    所以正经的语言不管编译不编译,都不会去支持这样的特性。方便编译优化,是另一个顺带的副作用罢了。
    当然,不是所有的简单明显语义等价性都该教条主义地被保留,例如破坏 eta equivalence 允许函数调用带有可观察的副作用,很大程度上是可取的。但是破坏 alpha renaming 并没什么这样显然的好处还会添乱,得不偿失。
    但造成 LZ 问题的不只是不理解为什么语言需要这样设计。更深层次的问题是,为什么需要反射来提取这样的信息?
    说白了也不复杂——语言没提供足够的机制(例如通用的 AST 接口)让用户自然地以一致的方式选取如何保留源代码中的信息,以至于表面看起来不复杂的需求突然就土法炼钢了。
    也不是特地需要婊 Java (几乎所有静态语言都有类似的毛病),但 Java 这种典型钦定编译 phase 没 homoiconicity 翻译时元编程又叒鸡的静态语言,就是这种先天不足的典型。所以没特地适应如何避免这种缺陷的用户遇到这类问题,以及一般的解决方案只能绥靖,就见怪不怪了。
    greenlaw110
        73
    greenlaw110  
       2019-12-02 10:09:45 +08:00
    LZ 提出的这个问题基本上就是 [ActFramework]( https://github.com/actframework/actframework) 处理 Controller 方法中的 render 参数列表解决的问题.

    对于下面的控制器方法:

    ```java
    @GetAction("/test")
    public void testPage(String name, int age) {
    boolean flag = ...;
    render(name, age, flag);
    }
    ```

    上面的代码最后 `render` 那句就是需要为模板引擎生成一个 Map 结构:

    ```
    "name" -> name 的值
    "age" -> age 的值
    "flag" -> flag 的值
    ```

    这个操作按照常规方式无法完成. ActFramework 的做法是在加载控制器类的时候做 ASM 扫描, 从 LVT (LocalVariableTable) 区拿到变量 id 对应的变量名字, 然后再用 ASM 做类增强, 生成的方法用 Java 语言表达为:

    ```java
    @GetAction("/test")
    public void testPage(String name, int age) {
    boolean flag = ...;
    ActionContext context = ActionContext.current();
    context.renderArg("name", name);
    context.renderArg("age", age);
    context.renderArg("flag", flag);
    throw new RenderAny();
    }
    ```

    扫描的逻辑参考 [这段代码]( https://github.com/actframework/actframework/blob/master/src/main/java/act/controller/bytecode/HandlerEnhancer.java#L86)

    增强的逻辑参考 [这个类]( https://github.com/actframework/actframework/blob/master/src/main/java/act/controller/bytecode/HandlerEnhancer.java#L140)
    lnchy
        74
    lnchy  
       2019-12-02 10:30:32 +08:00
    @wysnylc 对象引用不是值传递吗? java 采用的不是引用调用吧。传递到方法的参数也是在调用的时候申请指针把值传入,在方法结束之后就自动释放了。
    wysnylc
        75
    wysnylc  
       2019-12-02 13:22:30 +08:00
    @lnchy #74 java 有值传递和引用传递,基本类型和基本类型的封装类型是值传递,对象是引用传递
    你肯定不是 java 的,因为 java 默认学习的是引用传递
    wysnylc
        77
    wysnylc  
       2019-12-02 14:55:25 +08:00
    @greenlaw110 #76 Java 对象是引用传递,基本类型和基本类型的封装类型是值传递
    https://blog.csdn.net/Norte_L/article/details/80250057
    https://www.zhihu.com/question/31203609?sort=created
    你可能被这句话"一切传引用其实本质上是传值"误导了
    greenlaw110
        78
    greenlaw110  
       2019-12-02 15:41:35 +08:00
    @wysnylc 我没有被哪句话误导.

    我觉得还是先把概念定义清楚更好. 传引用这个概念我最早是从 C++ 语言学习到的. 下面是 C++ 一个传引用的例子:

    void swapnum(int &i, int &j) {
    int temp = i;
    i = j;
    j = temp;
    }

    这里实际上体现了传引用一个很基本的语义就是**引用是可以被重写的**. 在 C++ 中调用 swapnum 函数之后 i 和 j 的值会互换.

    把上面的例子用 Java 来写就是:

    void swapnum(Integer i, Integer j) {
    Integer temp = i;
    i = j;
    j = temp;
    }

    你觉得 Java 能够实现交换吗? 稍微做一个小实验就知道调用 swapnum 根本就不会交换 i 和 j 的值. 原因在于 Java 根本就不传引用, 传的只是某个堆上对象指针的值.
    greenlaw110
        79
    greenlaw110  
       2019-12-02 15:54:01 +08:00
    @wysnylc 我刚刚看了你贴的两个链接, 我基本上可以肯定这两个作者把"传引用"和 Java 的值类型 (基本类型, 引用类型) 搞混淆了. 文中所谓 "传引用" 实际上是传 "引用类型" 对象的值 (就是堆上的地址).

    这里有关于 Java 传值还是传引用更详细的说明:
    https://www.geeksforgeeks.org/g-fact-31-java-is-strictly-pass-by-value/

    另外你可以到 google 上搜索 "java pass by reference" 会有一堆类似的文中, 你基本上不会找到认定 Java 是传引用的.
    wysnylc
        80
    wysnylc  
       2019-12-02 16:46:27 +08:00
    @greenlaw110 #79 那么你认为如果一个方法传递了一个非基本类型和基本类型封装类型的对象参数,在方法内无论怎么修改这个对象,对原来的对象都是无影响的?因为传递的都是对象的副本所以不会影响到原对象是吧
    wysnylc
        81
    wysnylc  
       2019-12-02 16:51:21 +08:00
    @greenlaw110 #79 https://www.baeldung.com/java-pass-by-value-or-pass-by-reference 这是根据 java pass by reference 搜出来的结果,底下有这样的内容总结
    翻译一下:
    1.对于基本类型,参数是按值传递
    2.对于对象类型,对象引用是按值传递
    那么他这里是说将"引用"当成了对象的值传递
    所以我开始说你被误导了,关键在于"值"的定义
    wysnylc
        82
    wysnylc  
       2019-12-02 16:55:45 +08:00
    @greenlaw110 #78 这里没有交换是因为他们是基本类型,如果你理解的值传递和引用传递是"值传递为传递对象的值的副本,引用传递为传递对象本身" 那么你认为 java 没有引用传递你就错了
    而如果你认为"引用"是值的一种所以 java 只是值传递,那么你就被"一切传引用其实本质上是传值"误导了
    greenlaw110
        83
    greenlaw110  
       2019-12-02 18:12:10 +08:00
    @wysnylc 我觉得还是从字面上来讲比较好一点.

    中文 英文
    传值 pass by value
    传引用 pass by reference

    这里的中文表达并没有清晰地传递英语原意. 更准确地将应该是:

    按照值方式传递函数形参 - 有拷贝发生
    按照引用方式传递函数形参 - 无拷贝发生

    你刚刚引用的 java-pass-by-value-or-pass-by-reference 中有下面一段话

    A Java object, in contrast to Primitives, is stored in two stages. The reference variables are stored in stack memory and the object that they're referring to, are stored in a Heap memory.

    Whenever an object is passed as an argument, an exact copy of the reference variable is created which points to the same location of the object in heap memory as the original reference variable.


    上面第一部分将 Java 存放引用类型的机制说明地很清楚, 引用变量存储的是引用类型数据的内存地址, 也就是说变量本身的值就是引用类型数据的指针. 正因为如此, 传递引用变量的时候对值做了拷贝, 你将引用类型数据的地址通过值传递(拷贝)进了函数形参, 所以你可以操作这个引用类型数据, 但引用本身是没有传递进函数的, 因此我上面的 swapnum 函数不可能交换数据. 这个机制在第二部分说明的很清楚. 你上面说没有交换是因为他们是基本类型, 这个说法完全不正确, 因为在我上面的代码中我使用的是 Integer 而不是 int, 基本类型的包装类型, 即引用对象类型.

    另一方面讲, 中文的表达 "传值" 和 "传引用" 也有 "传递值到函数形参" 和 "传递对象引用到函数形参" 的味道, 这个可能是中文传值, 传引用容易引起误解的部分原因吧.
    greenlaw110
        84
    greenlaw110  
       2019-12-02 18:22:15 +08:00
    @wysnylc 我不清楚你为啥总是说我被 "一切传引用其实本质上是传值" 这句话误导. 实际情况是我在今天之前没有看到过这句话, 而且我也完全不同意这句话的说法. 传值隐含的语义是拷贝, 传引用是没有拷贝发生的, 这句话根本上就是错的.
    wysnylc
        85
    wysnylc  
       2019-12-02 18:38:22 +08:00
    @greenlaw110 #84 两种方式的本质是相同的,不同处在于对"值"的解释不同
    你认为引用是没有拷贝发生,但是我认为引用是引用原来的对象
    以后我还是改口成值传递,保持一致比较好,我们的本意是一样的
    greenlaw110
        86
    greenlaw110  
       2019-12-02 18:52:11 +08:00   ❤️ 1
    @wysnylc 嗯嗯, 这个地方的确有点绕. 因为我以前做过 C++ 项目,对引用有另外的了解. 总的来说引用是一种安全地传递指针(即地址)的方式, 但这个地址不是 Java 引用类型对象的地址, 而是当前堆栈上变量的指针. 如果说一种简单地方法来判断是否为引用传递, 只需要看看方法内的引用改变会不会反馈到调用方法(外层堆栈)上就可以了. 也就是我上面那个 swapnum 方法做的事情
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2884 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 12:37 · PVG 20:37 · LAX 04:37 · JFK 07:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.