Java 将泛型对象强制转换为泛型类

Java 将泛型对象强制转换为泛型类,java,android,reflection,generic-programming,Java,Android,Reflection,Generic Programming,我希望将泛型对象传递到我的方法中,并让它获得属性名称、类型和值 这是我的班级 public class Login { public String token; public String customerid; public Class1 class1; public Class2 class2; public class Class1 { public Class3 class3; public String

我希望将泛型对象传递到我的方法中,并让它获得属性名称、类型和值

这是我的班级

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();
    }
试试看{
//循环所有字段并为每个字段添加信息
for(字段:obj.getClass().getDeclaredFields()){
如果(!field.isSynthetic()){
字段。setAccessible(true);
System.out.println(“用户首选项”+field.getName()+“-”+field.getType()+“-”+field.get(obj));
}
}
//对于任何内部类,递归调用此方法并添加结果
//(这将反过来对该子类的所有子类执行此操作)
for(类子类:obj.getClass().getDeclaredClasses()){
objectsubobject=subClass.cast(obj);//问题
addUserPreferences(子对象,prefs);
}
}捕获(IllegalArgumentException e){
e、 printStackTrace();
}捕获(非法访问例外e){
e、 printStackTrace();
}catch(ClassCastException e){
e、 printStackTrace();
}
获取子对象(在本例中为
Class1
Class2
)并将其传递给方法是我遇到的问题。我尝试使用类而不是对象,但无法从类中获取对象

是否有任何方法可以将我传入的对象强制转换为子类


谢谢

您有几个选择:


一个选项是考虑定义一些定义用户偏好的对象的接口,例如:

interface UserPreferenceProvider {
    Map<String,Object> getUserPrefences();
}
其中,它们的
getUserPreferences()
实现返回要写入的首选项

然后您可以更改
addUserPreferences()
以获取
UserPreferenceProvider
,当您遍历字段时,检查是否找到
UserPreferenceProvider
,如果找到,将其转换为该提供者并将其传递给
addUserPreferences()

这也能更准确地表达你的意图。我相信这里的基本问题是你有这些你试图处理的任意对象,虽然它们在概念上有一些共同点,但是你的代码并没有代表这个概念;我知道这有点模糊,但由于没有让代码反映这一点,您现在面临着一项棘手的任务,即必须找到一种方法,强制以通用方式处理任意对象


第二个选项是创建自定义注释,例如
@UserPreference
,并使用该注释标记要写入的字段。然后,您可以遍历字段,当您找到带有此注释的字段时,将其单键/值添加到用户首选项中(即,对字段本身进行操作,而不是将整个容器类传递给
addUserPreferences()

这可能比您的设计的第一个选项更合适,也可能不合适。它的优点是不必强制您使用这些接口,也不必为
getUserPreferences();它还为您提供了对导出哪些属性的细粒度控制——本质上,这将您的注意力从对象转移到单个属性本身。这将是一种非常干净的方法,代码最少

如果你已经有了bean风格的getter,一个可能的方法是使用例如获取值,而不是滚动你自己的值;但对于您的情况,这是反射的一个非常基本的用法,可能不值得额外依赖


下面是获取使用自定义注释标记的对象的字段的名称和值的示例。第二个注释用于标记包含对象的字段,这些对象应递归降级并扫描。这很简单:

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());
    }

}
请注意,输出中不存在“int1”,因为它没有标记。你可以

仍然可以找到原始的基本注释示例


顺便说一句,你可以用注释做各种有趣的事情,例如,添加可选参数,让你覆盖首选项中的字段名,添加一个参数,让你指定一个自定义对象->用户首选项字符串转换器,等等。

我想出了一个简单的方法来做这件事。任何人谁有建议,使这更好或有问题的代码,请评论。下面的代码确实适用于我

    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();
    }
试试看{
类objClass=obj.getClass();
列表子对象列表=新建ArrayList();
//循环所有字段并为每个字段添加信息
for(字段:objClass.getDeclaredFields()){
如果(!field.isSynthetic()){
if(isWrapperType(field.getType())){
System.out.println(“名称:”+field.getName()+“值:”+field.get(obj));
}
否则{
if(field.getType().isArray()){
Object[]fieldArray=(Object[])field.get(obj);
对于(int i=0;i
isWrapperType
来自我在堆栈溢出问题中找到的代码。我所做的只是将
String
int
添加到集合中。

对于选项2,
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
    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();
    }