繁体   English   中英

将通用对象转换为通用类

[英]Cast generic object to generic class

我想将一个通用对象传递到我的方法中,并让它获取属性名称,类型和值。

这是我的课

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

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

无论如何,有没有将我传递给子类的对象强制转换?

谢谢

您有几种选择:


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

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()转换为该值并将其传递给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来自我在堆栈溢出问题中找到的代码。 我所做的就是将Stringint添加到集合中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM