繁体   English   中英

Java泛型 - 类型转换问题

[英]Java generics - Type casting issue

我有一个泛型方法,它接受要更新的类的类和字段。 例如:

class A {
  private int a;
  private int b;
}

class B {
 private int c;
 private int d;
}

在运行时,如果我们将类类型传递为“A.class”并将fieldstoBeUpdated传递为“b”,那么访问该特定类的字段的getter / setter的最佳方法是什么,以便我们可以修改字段。

public <T> void find(T clazz, List<String> fieldsToBeUpdated) {
List<T> collectionList = findAll((Class<T>) clazz);
collectionList.parallelStream().forEach(p -> {
        if (clazz instanceof A) {
            fieldsToBeUpdated.parallelStream().forEach(classFieldName -> {
                switch(classFieldName) {
                case "a":((A)p).setA(10);
                break;
                case "b":((A)p).setB(20);
                break;
                }
            });
        }

        if (clazz instanceof B) {
            fieldsToBeUpdated.parallelStream().forEach(classFieldName -> {
                switch(classFieldName) {
                case "c":((B)p).setC(30);
                break;
                case "d":((B)p).setD(40);
                break;
                }
            });
        }
    });
}

我已经写了上面的代码来实现相同的目的。

但问题是我有30个这样的类作为参数传递给这个泛型方法,该类的字段列表要更新/修改。

编写30个if语句检查类类型然后键入将对象强制转换为该类,这不是正确的实现。

有没有更好的方法来实现同样的目标?

提前致谢。

您的AB类似乎都提供set/getCreatedTimeset/getUpdatedTime方法。

如果其他28个左右的类也提供了这些(如你的问题所暗示的那样),那么只需要一个包含这些方法的通用接口,就可以实现所有类。

然后,您可以将方法的泛型类型绑定到该接口,并放弃所有instanceof语句和后续显式转换。

唯一的缺点是,如果List中的字段名称与传递给方法的具体类不相关。

如果要强制执行此操作,可以使用对象上的反射来发现字段是否按名称存在。 然后,您可以通过记录警告(或您认为适当的任何机制)轻松处理任何缺失的字段。

注意

正如thijs-steel所提到的,如果你的“时间”方法共享相同的实现,你可以让你的30个类扩展一个只实现“时间”方法的公共抽象父类。

或者,您可以使用default方法,因为您显然使用的是Java 8。

interface I {
    // assuming parameters and return types here
    public void setCreatedTime(ZonedDateTime z);
    public void setUpdatedTime(ZonedDateTime z);
    public ZonedDateTime getCreatedTime();
    public ZonedDateTime getUpdatedTime();
}

// A, B etc. all implement I

public <T extends I> void find(T object, List<String> fieldsToBeUpdated) {
    fieldsToBeUpdated
    .parallelStream()
    .forEach(
        field -> {
            switch(field) {
                case "a": {
                    try {
                        object.getClass().getDeclaredField("a");
                        // we're good
                        object.setCreatedTime(...);
                    }
                    catch (NoSuchFieldException e) {
                        // TODO something
                    }
                    break;
                }
                // ...
            }
        });
}

更新

如果您的类根本不共享任何公共字段,您可能希望完全改变整个方法。

您可能希望使用继承并在每个类中拥有自己的find方法实现,而不是采用“一个通用方法适合所有”逻辑实现范例。

这将允许在每个实现中使用较小的switch语句,并在default情况下执行错误处理。

您还可以通过仍然使用一个带有T extends Findablefind方法(其中Findable声明“time”方法现在是find方法)来概括行为,并且只需在给定的T对象上调用find

甚至可以在FindableTimed之间单独关注,让你的类实现它们。

我先提取一个接口:

public interface TimeManipulator {
    public void setCreatedTime(long createdTime);
    public long getCreatedTime();
    public void setUpdatedTime(long createdTime);
    public long getUpdatedTime();
}

并将其应用于类:

class A implements TimeManipulator {
    ...
 }

class B implements TimeManipulator {
    ...
 }

然后你需要做的就是将T绑定到这个接口:

public <T extends TimeManipulator> void find(T p, List<String> fieldsToBeUpdated) {
    fieldsToBeUpdated.parallelStream().forEach(field -> {
        switch(field) {
        case "a":
        case "c":
            p.setCreatedTime(TimeUtils.toGMT(p.getCreatedTime(), ZoneOffset.of("+05:30")));
            break;
        case "b":
        case "d":p.setUpdatedTime(TimeUtils.toGMT(p.getUpdatedTime(), ZoneOffset.of("+05:30")));
        break;
        }
    });
}

使用@Mena给出的建议,我想出了一个使用反射API的解决方案。

我正在遍历fieldsToBeUpdated参数列表,并且在每次迭代中,我正在使用下面的一行来检查对象中是否存在该字段(作为参数传递):

如果存在,则返回字段对象null。

null != clazz.getDeclaredField(field)

以下是整个实现逻辑:

public <T> void find(Class clazz, List<String> fieldsToBeUpdated) {
    List<T> collectionList = db.findAll((Class<T>) clazz);
    if (CollectionUtils.isNotEmpty(collectionList)) {
        collectionList.stream().forEach(p -> {
            fieldsToBeUpdated.stream().forEach(field -> {
            Date date = null;
                try {
                 if (null != clazz.getDeclaredField(field)) {
                        Field f = clazz.getDeclaredField(field);
                        f.setAccessible(true);
                        date = (Date) f.get(p);
                    }
                } catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) {
                    e.printStackTrace();
                } 
            });
        });
    }
}

暂无
暂无

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

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