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

2019-11-30 09:59:47 +08:00
 so2back

好像不能发图片,我直接复制一下自己用来解释的代码,应该能看得懂说什么吧。。。我就想问问 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); }

10198 次点击
所在节点    Java
86 条回复
cyrbuzz
2019-11-30 19:31:43 +08:00
只有我一个人疑惑复制为啥不是 ctrl+C 而是+D 吗= =。
340244120w
2019-11-30 20:37:35 +08:00
编译的时候加上-parameters 这样就能通过反射获取到方法名了。SpringMvc controller 方法参数绑定就这样获取的
neoblackxt
2019-11-30 21:03:21 +08:00
@cyrbuzz 有一种 IDE 叫 intellij IDEA
cyrbuzz
2019-11-30 22:34:00 +08:00
@neoblackxt 我以为大部分都是 ctrl+d 选择同名所选单词= =。
nowto
2019-11-30 23:08:31 +08:00
只看代码。
我们知道,变量都是有作用域的,name、age、hobby 作为 main 方法的局部变量,只限于在 main 方法中使用。
而 generateMap 方法中并没有定义名字为 name、age、hobby 的局部变量,同时也没有成员变量是叫这些名字的,所以编译不会通过的。
ClericPy
2019-11-30 23:13:18 +08:00
该说的楼上们都说了...

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

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

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

而不是土法炼钢。。。
Antihank
2019-12-01 13:51:02 +08:00
不要气馁,虽然问题很幼稚但是你的想法是很好的,继续进步!
ooops
2019-12-01 13:57:44 +08:00
楼主问不明白还不自知,你没问题会有这么多人说你提问有问题么?好好审视一下自己,b 了再见。
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
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
2019-12-02 10:30:32 +08:00
@wysnylc 对象引用不是值传递吗? java 采用的不是引用调用吧。传递到方法的参数也是在调用的时候申请指针把值传入,在方法结束之后就自动释放了。
wysnylc
2019-12-02 13:22:30 +08:00
@lnchy #74 java 有值传递和引用传递,基本类型和基本类型的封装类型是值传递,对象是引用传递
你肯定不是 java 的,因为 java 默认学习的是引用传递
greenlaw110
2019-12-02 14:43:11 +08:00
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
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
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
2019-12-02 16:46:27 +08:00
@greenlaw110 #79 那么你认为如果一个方法传递了一个非基本类型和基本类型封装类型的对象参数,在方法内无论怎么修改这个对象,对原来的对象都是无影响的?因为传递的都是对象的副本所以不会影响到原对象是吧

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/624528

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX