英文:
Get the class field name in Java
问题
public class Product{
private String id;
private String name;
private String description;
// Getters and setters..
}
从上面的类中,我想在运行时获取“description”字段的名称:
Query query = new Query();
if (productSearchCriteria.getDescription().isPresent()) {
query.addCriteria(Criteria.where("description").is(productSearchCriteria.getDescription().get()));
}
目前,我已经硬编码了名称,但它应该是动态的,因为如果有人重命名或更改属性名称,这将无法工作。
在搜索中,我找到了一个 reflection-util 库。
String numberProperty = PropertyUtils.getPropertyName(Product.class, Product::getDescription);
这给了我一个异常:
Caused by: java.lang.ClassCastException: class domain.Product$ByteBuddy$QIcFk1aK cannot be cast to class domain.Product (domain.Product$ByteBuddy$QIcFk1aK is in unnamed module of loader net.bytebuddy.dynamic.loading.ByteArrayClassLoader @33943b71; domain.Product is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @5be4b0e6)
at de.cronn.reflection.util.PropertyUtils.findMethodByGetter(PropertyUtils.java:322) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyDescriptorCache.lambda$getMethod$4(PropertyDescriptorCache.java:178) ~[reflection-util-2.6.0.jar:na]
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) ~[na:na]
at de.cronn.reflection.util.PropertyDescriptorCache.getMethod(PropertyDescriptorCache.java:178) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyUtils.getMethod(PropertyUtils.java:315) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyUtils.getPropertyDescriptor(PropertyUtils.java:282) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyUtils.getPropertyName(PropertyUtils.java:290) ~[reflection-util-2.6.0.jar:na]
at fete.bird.fetebirdproduct.common.querybuilder.QueryBuilder.BuildQuery(QueryBuilder.java:24) ~[main/:na]
at fete.bird.fetebirdproduct.service.consumer.ProductListener.GetProducts(ProductListener.java:56) ~[main/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at ... 9 common frames omitted
我正在使用 JDK 14。我知道有一个 Java 反射 API。我不能硬编码字段名称。
是否有解决方案?
<details>
<summary>英文:</summary>
public class Product{
private String id;
private String name;
private String description;
// Getters and setters..
}
From the above class, I want to get the field name of description during runtime:
Query query = new Query();
if (productSearchCriteria.getDescription().isPresent()) {
query.addCriteria(Criteria.where("description").is(productSearchCriteria.getDescription().get()));
}
Currently, I have hardcoded the name, but it should be dynamic, because if someone renames or changes the property name, this will not work.
While looking around, I found a [reflection-util](https://github.com/cronn-de/reflection-util) library.
String numberProperty = PropertyUtils.getPropertyName(Product.class, Product::getDescription);
This gave me an exception:
Caused by: java.lang.ClassCastException: class domain.Product$ByteBuddy$QIcFk1aK cannot be cast to class domain.Product (domain.Product$ByteBuddy$QIcFk1aK is in unnamed module of loader net.bytebuddy.dynamic.loading.ByteArrayClassLoader @33943b71; domain.Product is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @5be4b0e6)
at de.cronn.reflection.util.PropertyUtils.findMethodByGetter(PropertyUtils.java:322) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyDescriptorCache.lambda$getMethod$4(PropertyDescriptorCache.java:178) ~[reflection-util-2.6.0.jar:na]
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) ~[na:na]
at de.cronn.reflection.util.PropertyDescriptorCache.getMethod(PropertyDescriptorCache.java:178) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyUtils.getMethod(PropertyUtils.java:315) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyUtils.getPropertyDescriptor(PropertyUtils.java:282) ~[reflection-util-2.6.0.jar:na]
at de.cronn.reflection.util.PropertyUtils.getPropertyName(PropertyUtils.java:290) ~[reflection-util-2.6.0.jar:na]
at fete.bird.fetebirdproduct.common.querybuilder.QueryBuilder.BuildQuery(QueryBuilder.java:24) ~[main/:na]
at fete.bird.fetebirdproduct.service.consumer.ProductListener.GetProducts(ProductListener.java:56) ~[main/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at ... 9 common frames omitted
I am using JDK 14. I know there is a Java reflection API. I can't hard code the field name.
Is there any solution?
</details>
# 答案1
**得分**: 6
你可以尝试使用 `lombok` 来生成字段名作为常量变量。
https://projectlombok.org/features/experimental/FieldNameConstants
```java
@Data
@FieldNameConstants
public class Product{
private String id;
private String name;
private String description;
}
这将在编译时生成:
public class Product {
private String id;
private String name;
private String description;
public static final class Fields {
public static final String id = "id";
public static final String name = "name";
public static final String description = "description";
}
// getters and setters
}
引用将会是这样的:
Query query = new Query();
if (productSearchCriteria.getDescription().isPresent()) {
query.addCriteria(Criteria.where(Product.Fields.description).is(productSearchCriteria.getDescription().get()));
}
这样做的额外好处是,如果出于任何原因你决定更改字段名,它会在编译时引发错误,可以在运行之前发现问题。
不需要运行时反射技巧。
英文:
You can try to use lombok
to generate field names as a constant variable.
https://projectlombok.org/features/experimental/FieldNameConstants
@Data
@FieldNameConstants
public class Product{
private String id;
private String name;
private String description;
}
This will be generated at compile time:
public class Product {
private String id;
private String name;
private String description;
public static final class Fields {
public static final String id = "id";
public static final String name = "name";
public static final String description = "description";
}
// getters and setters
}
And referencing would look like this:
Query query = new Query();
if (productSearchCriteria.getDescription().isPresent()) {
query.addCriteria(Criteria.where(Product.Fields.description).is(productSearchCriteria.getDescription().get()));
}
This has the added benefit of causing a compile time error if you decide to change the field name for whatever reason, catching it early before runtime.
No runtime reflection shenanigans needed.
答案2
得分: 4
以下是翻译好的内容:
有点奇怪,你想使用运行时的反射方法来检索实际上是常量的内容 - 你知道它就是字面上的字符串 "description",对于给定的编译运行,它将永远不变。
我能想象你想要这样做的唯一原因是,你担心你会重命名该字段,然后忘记重命名字符串常量。
许多基于IDE的字段重命名工具会发现与字段同名的字符串常量,并会提到它们("嘿,伙计?也许你想让我同时重命名这里的字符串常量?")。
如果对你来说这仍然不够好,你可以创建一个字符串常量,并紧接着放置它。例如:
public class Product{
private String id;
private String name;
private String description;
private static final String DESCRIPTION_PROPERTY_NAME = "description";
// Getter and setter
}
然后始终使用 DESCRIPTION_PROPERTY_NAME
代替 "description"
。
毫无疑问,没有人会只编辑一项内容,然后忘记编辑紧随其后的内容!
但是,如果不知何故你仍然担心,或者这已经变成了一种疯狂的学术练习 - 那么,这从根本上讲是一个编译时的问题,所以可以使用注解处理器,它是一个编译时的概念!也许可以尝试一下 Lombok 的 FieldNameConstants。
如果即使那样也不合适,实际上是没有办法的 - 你在 getDescription
方面所做的事情,最多只会给你 getter 的名称。而不是字段的名称。在没有注解处理器的普通 Java 中,如果没有其他工具支持,没有语言结构可以在字段重命名时无法编译通过。有一种语法可以引用方法(x::y
),但是没有类似的结构可以引用字段。
英文:
It's a bit weird that you want to use a runtime, reflective method to retrieve what is effectively a constant - you know it's literally the string "description" and will always be for a given compile run.
The only reason I can imagine that you want this, is that you're afraid that you're going to rename the field and that you will then forget to rename the string constant(s).
A bunch of IDE-based utilities for renaming fields will spot string constants that have the same name as the field and will mention them ('hey, buddy? Maybe you want me to rename this string constant here too?').
If that's still not good enough for you, you can make a string constant and put it immediately following. e.g:
public class Product{
private String id;
private String name;
private String description;
private static final String DESCRIPTION_PROPERTY_NAME = "description";
// Getter and setter
}
and then always use DESCRIPTION_PROPERTY_NAME
instead of "description"
.
Surely nobody is going to edit one thing and forget to edit the thing that is right on the following line!
But if somehow you're still worried or this has devolved into some crazy academic exercise - well, it is fundamentally a compile time concern, so use annotation processors which are a compile time concept! Perhaps try lombok's FieldNameConstants.
And if even that is no good, there is no actual way - what you've done there with the getDescription
aspect is, at best, going to give you the name of the getter. Not the name of the field. There is no way in annotation-processor-less vanilla java without any other tooling in place to use a language construct that will fail to compile if the field is renamed. There is a syntax to refer to a method (x::y
), but there is no such construct to refer to a field.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论