java - 将泛型对象转换为泛型类

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

获取子对象(在本例中为Class1Class2)并将其传递给方法是我遇到的问题。我尝试使用类而不是对象,但无法从类中获取对象。

是否可以将我传入的对象强制转换为子类?

谢谢

最佳答案

您有几个选择:

<小时/>

一种选择是考虑定义一些接口(interface)来定义提供用户首选项的对象,例如:

interface UserPreferenceProvider {
    Map<String,Object> getUserPrefences();
}

然后你可以让你的类实现该接口(interface),例如:

public class Login implements UserPreferenceProvider {
    ...
    public class Class1 implements UserPreferenceProvider {
        ...
        public class Class2 implements UserPreferenceProvider {
            ...
        }
    }
}

他们的getUserPreferences()在哪里实现返回要写入的首选项。

然后你可以更改addUserPreferences()采取UserPreferenceProvider ,当你遍历字段时,检查是否找到 UserPreferenceProvider如果是这样,则将其转换为该值并将其传递给 addUserPreferences() .

这也能更准确地表达您的 Intent 。我认为这里的根本问题是您尝试使用这些任意对象,虽然从概念上讲它们有一些共同点,但您的代码并不代表该概念;我知道这有点模糊,但由于没有让您的代码反射(reflect)这一点,您现在面临着必须找到一种方法来强制以通用方式处理任意对象的尴尬任务。

<小时/>

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

这可能比第一个选项更适合您的设计,也可能不更合适。它的优点是不强制您使用这些接口(interface),并且不必编写代码将数据打包到 map 或其他 getUserPreferences() 中。 ;它还可以让您更细粒度地控制导出哪些属性——本质上,这会将您的注意力从对象转移到各个属性本身。这将是一种非常干净的方法,代码最少。

如果你已经有了 bean 风格的 getters,一种可能的方法是使用例如: 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”不存在于输出中,因为它没有被标记。您可以run the example on ideone .

原来的基本标注示例仍然可以找到here

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

关于java - 将泛型对象转换为泛型类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23067213/

相关文章:

android - 如何使用设备农场为 BOOT_COMPLETED 和 USER_PRESENT 接收器创建自动化测试?

java - 如何正确使用反射访问 Telephony Manager 中的隐藏方法

java - 如何在休息服务中的java对象不使用@XmlRootElement的情况下获取请求对象

java - 我如何为我的 Java 应用程序的用户前端决定是使用 Swing GUI 还是轻量级 Web 客户端?

java - 为什么Java在理论上是平台独立的,而在实践中是平台依赖的?

Android Studio模拟器: process exit with error code 1

java - 如何有效控制实现多接口(interface)的类对象?

java - TabLayout 中点之间的间距

java - getTypeParameters 返回一个空的 TypeVariable 数组

python - python中的方法委托(delegate)