我想将一个泛型对象传递到我的方法中,并让它获得属性名称、类型和值。
这是我的班级
public class Login {
public String token;
public String customerid;
public Class1 class1;
public Class2 class2;
public class Class1 {
public Class3 class3;
public String string1;
public class Class3 {
public int int1;
public String string2;
public String string3;
}
}
public class Class2 {
public int int1;
public String string2;
public String string3;
}
}
我希望输出看起来像这个
User Preferences customerid - class java.lang.String - 586969
User Preferences token - class java.lang.String - token1
User Preferences string1 - class java.lang.String - string1Value
User Preferences string2 - class java.lang.String - string2Value
User Preferences string3 - class java.lang.String - string3Value
我现在掌握的代码给我带来了问题。这是代码:
try {
// Loop over all the fields and add the info for each field
for (Field field : obj.getClass().getDeclaredFields()) {
if(!field.isSynthetic()){
field.setAccessible(true);
System.out.println("User Preferences " + field.getName() + " - " + field.getType() + " - " + field.get(obj));
}
}
// For any internal classes, recursively call this method and add the results
// (which will in turn do this for all of that subclass's subclasses)
for (Class<?> subClass : obj.getClass().getDeclaredClasses()) {
Object subObject = subClass.cast(obj); // ISSUE
addUserPreferences(subObject, prefs);
}
}catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}catch(ClassCastException e) {
e.printStackTrace();
}
获取子对象,在本例中为Class1
或Class2
,并将其传递给方法是Im遇到的问题。我尝试使用类而不是对象,但无法从类中获取对象。
是否有将我传递的对象强制转换到子类的方法?
感谢
您有几个选项:
一种选择是考虑定义一些界面,定义提供用户偏好的对象,例如:
interface UserPreferenceProvider {
Map<String,Object> getUserPrefences();
}
然后你可以让你的类实现这个接口,例如:
public class Login implements UserPreferenceProvider {
...
public class Class1 implements UserPreferenceProvider {
...
public class Class2 implements UserPreferenceProvider {
...
}
}
}
其中它们的getUserPreferences()
实现返回要写入的首选项。
然后,您可以将addUserPreferences()
更改为UserPreferenceProvider
,并且在遍历字段时,检查是否找到UserPreferenceProvider
,如果找到,则将其强制转换为addUserPreferences()
。
这也将更准确地代表你的意图。我认为这里的根本问题是,你有这些任意的对象,你正在尝试使用它们,虽然在概念上它们有一些共同点,但你的代码并不能代表这个概念;我知道这有点模糊,但由于没有让你的代码反映这一点,你现在面临着一项尴尬的任务,那就是必须找到一种方法来强迫你的任意对象以通用的方式处理。
第二种选择可以是创建一个自定义注释,例如@UserPreference
,并使用它来标记要编写的字段。然后,您可以遍历字段,当您找到带有此注释的字段时,将其单个键/值添加到用户首选项中(即,对字段本身进行操作,而不是将整个容器类传递给addUserPreferences()
)。
这可能比您的设计的第一个选项更合适,也可能不合适。它的优点是不必强迫您使用这些接口,也不必编写代码将数据打包到getUserPreferences()
的映射或其他内容中;它还为您提供了对导出哪些属性的细粒度控制——本质上,这将您的注意力从对象转移到各个属性本身。这将是一种非常干净的方法,只需最少的代码。
如果你已经有了bean风格的getter,那么一种更方便的方法是使用例如Apache BeanUtils来获取值,而不是滚动自己的值;但对于您的情况,这是对反射的一种非常基本的使用,可能不值得额外依赖。
下面是一个获取使用自定义注释标记的对象的字段的名称和值的示例。第二个注释用于标记包含应该递归下降到并扫描的对象的字段。这很简单:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
// @UserPreference marks a field that should be exported.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface UserPreference {
}
// @HasUserPreferences marks a field that should be recursively scanned.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface HasUserPreferences {
}
// Your example Login class, with added annotations.
class Login {
@UserPreference public String token; // <= a preference
@UserPreference public String customerid; // <= a preference
@HasUserPreferences public Class1 class1; // <= contains preferences
public class Class1 {
@HasUserPreferences public Class2 class2; // <= contains preferences
@UserPreference public String string1; // <= a preference
public class Class2 {
public int int1; // <= not a preference
@UserPreference public String string2; // <= a preference
@UserPreference public String string3; // <= a preference
}
}
// Construct example:
public Login () {
token = "token1";
customerid = "586969";
class1 = new Class1();
class1.string1 = "string1Value";
class1.class2 = class1.new Class2();
class1.class2.string2 = "string2Value";
class1.class2.string3 = "string3Value";
}
}
public class ValueScanExample {
// Recursively print user preferences.
// Fields tagged with @UserPreference are printed.
// Fields tagged with @HasUserPreferences are recursively scanned.
static void printUserPreferences (Object obj) throws Exception {
for (Field field : obj.getClass().getDeclaredFields()) {
// Is it a @UserPreference?
if (field.getAnnotation(UserPreference.class) != null) {
String name = field.getName();
Class<?> type = field.getType();
Object value = field.get(obj);
System.out.println(name + " - " + type + " - " + value);
}
// Is it tagged with @HasUserPreferences?
if (field.getAnnotation(HasUserPreferences.class) != null) {
printUserPreferences(field.get(obj)); // <= note: no casts
}
}
}
public static void main (String[] args) throws Exception {
printUserPreferences(new Login());
}
}
输出为:
token - class java.lang.String - token1
customerid - class java.lang.String - 586969
string2 - class java.lang.String - string2Value
string3 - class java.lang.String - string3Value
string1 - class java.lang.String - string1Value
请注意,输出中不存在"int1",因为它没有被标记。你可以在ideone上运行这个例子。
原始的基本注释示例仍然可以在这里找到。
顺便说一句,你可以用注释做各种有趣的事情,例如,添加可选参数,让你覆盖首选项中的字段名,添加一个参数,让我们指定一个自定义对象->用户首选项字符串转换器,等等。
我已经找到了一种简单的方法。任何人如果有改进建议或对代码有问题,请发表评论。下面的代码确实适用于我
try {
Class<?> objClass = obj.getClass();
List<Object> subObjectList = new ArrayList<Object>();
// Loop over all the fields and add the info for each field
for (Field field: objClass.getDeclaredFields()) {
if(!field.isSynthetic()){
if(isWrapperType(field.getType())){
System.out.println("Name: " + field.getName() + " Value: " + field.get(obj));
}
else{
if(field.getType().isArray()){
Object[] fieldArray = (Object[]) field.get(obj);
for(int i = 0; i < fieldArray.length; i++){
subObjectList.add(fieldArray[i]);
}
}
else{
subObjectList.add(field.get(obj));
}
}
}
}
for(Object subObj: subObjectList){
printObjectFields(subObj);
}
}catch(IllegalArgumentException e){
// TODO Auto-generated catch block
e.getLocalizedMessage();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.getLocalizedMessage();
}
isWrapperType
来自我在这个堆栈溢出问题中发现的代码。我所做的只是将String
和int
添加到集合中。