Java中的等效于C#中的nameof的方法是什么?

49

C# 6.0 引入了 nameof() 运算符,它返回一个字符串,表示放在其中的任何类/函数/方法/局部变量/属性标识符的名称。

如果我有一个像这样的类:

class MyClass
{
    public SomeOtherClass MyProperty { get; set; }

    public void MyMethod()
    {
        var aLocalVariable = 12;
    }
}

我可以这样使用运算符:

// with class name:
var s = nameof(MyClass); // s == "MyClass"

// with properties:
var s = nameof(MyClass.OneProperty); // s == "OneProperty"

// with methods:
var s = nameof(MyClass.MyMethod); // s == "MyMethod"

// with local variables:
var s = nameof(aLocalVariable); // s == "aLocalVariable".

这很有用,因为正确的字符串在编译时进行检查。 如果我拼写某个属性/方法/变量的名称不正确,编译器将返回错误。 此外,如果我重构代码,所有字符串都会自动更新。 例如,有关实际用例,请参见此文档

Java中是否有该运算符的等效物?否则,如何实现相同的结果(或类似结果)?


2
很确定Java没有完全等效的东西。所以像C# 6之前,你可能只能使用字符串字面量。 - juharr
5
你想通过拥有这种能力来解决什么问题? - OldProgrammer
3
我经常在对象更新事件中使用它。我将 nameof(AffectedProperty) 传递给事件参数对象的相关属性,以通知监听器哪个属性已被更新。这是一种获取属性名称编译时安全性的好方法。 - Abion47
8
问题并不是关于C#,而是关于Java,因此标签不相关。它包含和提到了C#,但这并不能证明需要添加该标签。请参见http://meta.stackoverflow.com/a/319194/1743880。 - Tunaki
2
我会把这个加入到C#可以做而Java不能做的事情列表中。*除非通过字节码操作。 - Josh M.
显示剩余8条评论
6个回答

22

1
那么我必须使用外部库吗?不用了,这应该与Java语言一起打包,并能够在Javadoc注释中引用参数名称。 - Mike Lowery

20

很遗憾,这样的功能并不存在。我曾经在一段时间内寻找过这个功能,答案似乎是通常情况下,此类功能是不存在的。

参见 获取字段名称

当然,你可以为你自己的类使用“Named”注解来实现这个目标。实际上,有很多依赖于类似概念的框架。即使如此,这也不是自动完成的。


2
似乎随着Java 8的出现,情况已经发生了变化。我在下面分享了可能的解决方案。 - jreznot

6
你无法这样做。
你可以使用反射获取方法或字段,但必须将方法名硬编码为字符串,这就消除了整个目的。
与C#不同,Java没有内置属性概念。Getter和setter只是普通方法。你甚至不能像在问题中那样轻松地引用一个方法。你可以尝试使用反射来获取getter方法的句柄,然后切掉get以获取它所表示的“属性”的名称,但这很丑陋,而且不同。
至于局部变量,根本不可能实现。

1
这个问题与C#属性无关。问题是在询问是否可以在不使用名称本身的情况下获取方法或字段的名称。你提到反射需要名称来获取方法或字段,这一点很相关。 - Andy Thomas
1
好的,我提到了C#属性,但显然它们在Java中不存在。那只是一个例子。我的问题的重点是获取类成员/变量/字段/标识符的字符串表示形式,而你给出了一些有用的答案。 - Massimiliano Kraus
1
@f1sh - 你是对的 - 在一个例子中提到了一个属性。 "Germane" 的意思是相关的。我编辑了你的答案,将最相关的部分放在了前面,并取消了踩。我认为可以通过指出你可以获取类的名称来进一步改进答案。 - Andy Thomas
1
使用反射,您可以枚举方法和“属性”,因此可以在不预先硬编码它们的情况下发现名称 - 即.getClass().getDeclaredMethods().getClass().getDeclaredFields() - Stephen P
3
@StephenP 这是真的。但如果你想获取特定方法/字段的名称,你必须使用equals或contains检查方法/字段的名称,这需要再次将名称键入为字符串文字。 - f1sh
显示剩余4条评论

4

Lombok有一个实验性特性@FieldNameConstants

添加注释后,您将获得具有字段名称的内部类型Fields

@FieldNameConstants
class MyClass {
 String myProperty;
}
...

String s = MyClass.Fields.myProperty; // s == "myProperty"

3

你无法这样做。

如果使用调试符号进行编译,则.class文件将包含变量名称表(这是调试器将变量映射回源代码的方式),但不能保证其存在,并且在运行时不会公开。


1
该问题首先询问编译代码中包含的类、方法和字段的名称,以及通过反射公开的名称。 - Andy Thomas
1
@AndyThomas 不,它不会。停止对正确答案的投反对票。 - f1sh
2
@f1sh - “返回一个字符串,表示放置在内部的成员/类/字段的名称” - Andy Thomas
你不需要调试符号来获取类、方法和字段的名称(请参见我对f1sh答案的评论)。你确实需要它们来获取局部作用域中声明的任意变量的名称,但这不是OP所问的。 - Stephen P

2
我也很烦恼Java中没有类似的东西,所以我自己实现了它:https://github.com/mobiuscode-de/nameof 你可以像这样简单地使用它:
Name.of(MyClass.class, MyClass::getProperty)

这将只返回字符串。
"property"

它也在Maven Central上,因此您可以像这样将其添加到您的项目中:
<dependency>
    <groupId>de.mobiuscode.nameof</groupId>
    <artifactId>nameof</artifactId>
    <version>1.0</version>
</dependency>

或者对于Gradle:
implementation 'de.mobiuscode.nameof:nameof:1.0'

我意识到这很类似于 strangeway 的库,但我认为最好不要引入奇怪的$/$$符号和增强的字节码工程。我的库只是使用代理类,在其上调用getter来确定传递的方法名称。这允许简单地提取属性名称。
我还创建了一篇关于该库更多细节的博客文章

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接