获取类的参数名称以将其传递给java

时间:2015-06-12 08:02:41

标签: java reflection parameters

我想将参数名称作为参数传递给其他方法,f.e:

我上课了:

public class Foo() {
    public Bar bar;
    public Bar anotherBar;
    public Bar yetAnotherBar;

    public void doSomethingWithBar() {
        common.doingSomething(
            getMostImportantBarParameterName()
        );
    }
}

在这堂课中我会有方法:

public String getMostImportantBarParameterName() {
    return Foo.bar;
}

而不是返回值的bar,我想获得一个参数名称吧,所以它应该只返回"bar"

现在我必须这样做:

public String getMostImportantBarParameterName() {
    return "bar";
}

为什么我想要达到这样的目标? 我尽我所能避免在我的代码中使用字符串,因为在重构过程中我会偶然绕过(跳过)它。

但是,如果我将以这种方式使用“硬编码”参数,当我稍后重命名此参数时,Eclipse IDE将自动替换它(使用LALT + LSHIFT + R)

另外我的方法:common.doingSomething()在运行时使用参数,所以我不会得到编译错误,这很难维护这个方法。

我不写单元测试,因为我还不能。

请给我一些帮助。感谢

-----------------编辑------------------------

现实生活中使用。

我想有通用方式访问数据库记录的方法。 我的应用程序中的常见数据库操作是:

TableName获取Parameter = SomeValue

的记录

所以我想在下面列出的通用实体中使用泛型方法:

@MappedSuperclass
public abstract class GenericModel<T extends GenericModel> {
    @Transient protected Class<T> entityClass;
    private List<T> getByParameterAndValue(String parameter, String value) {
        List<T> entities = new ArrayList<T>();
        String sqlString = "SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e."+ parameter + " = :value";
        TypedQuery<T> query = JPA.em().createQuery(sqlString, entityClass).setParameter("value", value);
        try {
            entities = query.getResultList();
        } catch (NoResultException e1) {
            entities = null;
        } catch (Exception e) {
            Index.toLog("error","Unsupported error in Generic model class in " + entityClass);
        }
        return entities;
    }

由真实实体f.e。扩展:

public class User extends GenericModel<User> {
    public String name;
    public String email;
    public String date;
    public String department;

    public List<User> getUsersByDepartments(String dep) {
        return getByParameterAndValue("department", dep);
    }
}

问题在于JPA TypedQuery

TypedQuery<User> query = em.createQuery("SELECT u FROM User u WHERE u.department = :department", User.class);
return query.setParameter("department", department).getSingleResult();

2 个答案:

答案 0 :(得分:2)

首先,我认为您应该重新考虑您的方法。使用这样的字段名称(通过反射或硬编码的字符串)不是很健壮。一般来说,如果可能,应该避免反思。

你想要达到什么目的? common.doingSomething对字段名称的处理方式是什么?

使用访问器明确地模拟重要性可能更好:

class Foo {

    private Bar bar;
    private Bar anotherBar;
    private Bar yetAnotherBar;

    public Bar getMostImportantBar() {
        return bar;
    }
}

回答关于泛型的问题。您可以按索引或名称选择字段。两者都不健壮,因为当您更改字段名称时,用于通过反射获取它的字符串不会随之改变,如果您更改字段的顺序,则索引将是错误的。

以下是如何操作:

Class foo = Foo.class;
Field[] fields = foo.getFields();

// get by index
Field firstField = fields[0];
String firstFieldName = firstField.getName();

// get by name
Field barField = foo.getField("bar");   
String barFieldName = barField.getName();

编辑(阅读更新后的问题):

在任何对象关系映射解决方案中,都存在面向对象领域结束且关系领域开始的边界。使用您的解决方案,您可以将该边界进一步拉入代码中,以便轻松使用特定的模型类和查询。这样做的结果是你得到了更多的锅炉板&#39;样式代码作为应用程序的一部分(GenericModel类),边界变得更加可见(使用反射通过索引或名称引用字段)。这种类型的代码通常难以理解,测试和维护。另一方面,一旦你做对了,它就不会经常改变(如果你对你通常需要的查询类型的假设证明是有效的)。

所以我认为这不是一个荒谬的反思用例,即使我自己可能仍然坚持JPA并接受查询的相似性。有了一个很好的JPA框架,表达这些查询不会产生很多代码。

关于硬编码的字段名称与索引,我建议您使用字段名称,因为它们更容易理解和调试您的后继者。我会确保字段名称在字段所在的模型类中表示,以使两者尽可能清晰,类似于您给出的示例:

public class User extends GenericModel<User> {

    public static final String FIELD_NAME = "name";
    public static final String FIELD_EMAIL = "email";
    public static final String FIELD_DATE = "date";
    public static final String FIELD_DEPARTMENT = "department";

    private String name;
    private String email;
    private String date;
    private String department;

    // the byXXX naming scheme is a quite common shorthand for lookups
    public List<User> byDepartment(String department) {
        return getByParameterAndValue(FIELD_DEPARTMENT, department);
    }

BTW我认为getByParameterAndValue不能是私有的(必须至少是默认的)。另外,我认为你不应该在开始时初始化List<T> entities = new ArrayList<T>()。您可以在catch(Exception e)中执行此操作,以避免在查询成功或不返回任何结果时进行不必要的初始化。您的字段应该是私有的(如上所示)。

当然,这种方法仍然会为每个字段生成一种查找方法。另一种解决方案是为此创建服务并使模型对象保持友好(没有行为):

public class DaoService {

    public <T extends GenericModel> List<T> get(Class<T> entityClass, String fieldName, String value) {
        List<entityClass> entities;
        String sqlString = "SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e."+ fieldName+ " = :value";
        TypedQuery<T> query = JPA.em().createQuery(sqlString,     entityClass).setParameter("value", value);
        try {
            entities = query.getResultList();
        } catch (NoResultException e) {
            entities = null;
        } catch (Exception e) {
            entities = new ArrayList<T>()            
        }
        return entities;
    }
}

用法:

List<User> = daoService.get(User.class, User.FIELD_DEPARTMENT, value);

这是我刚才的另一个(略显狂野的)想法。每个模型类也是一个查询模板:

public abstract class ModelQuery<T extends ModelQuery> {

    // TODO set from constructor
    private Class<T> entityClass;
    private Field[] allFields = entityClass.getFields();

    private List<T> getByTemplate() {
        List<Field> queryFields = new ArrayList<Field>();
        String sql = selectFieldsAndCreateSql(queryFields);
        TypedQuery<T> query = setQueryParameters(queryFields, sql);
        return executeQuery(query);
    }

    private String selectFieldsAndCreateSql(List<Field> queryFields) throws IllegalAccessException {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT e FROM ")
                .append(entityClass.getSimpleName())
                .append("e WHERE ");
        for (Field field : allFields) {
            if (field.get(this) != null) {
                sql.append("e.")
                        .append(field.getName())
                        .append(" = :")
                        .append(field.getName());
                // keep track of the fields used in the query
                queryFields.add(field);
            }
        }
        return sql.toString();
    }

    private TypedQuery<T> setQueryParameters(List<Field> queryFields, String sql) throws IllegalAccessException {
        TypedQuery<T> query = JPA.em().createQuery(sql, entityClass);
        for (Field field : queryFields) {
            query.setParameter(field.getName(), field.get(this));
        }
        return query;
    }

    private List<T> executeQuery(TypedQuery<T> query) {
        List<T> entities;
        try {
            entities = query.getResultList();
        } catch (NoResultException e1) {
            entities = null;
        } catch (Exception e) {
            entities = new ArrayList<T>();
        }
        return entities;
    }

}

用法:

User userQuery = new User();
userQuery.setDepartment("finance");
List<User> results = userQuery.getByTemplate();

我想有更多的方法可以给这只猫留下皮肤。祝你找到最佳解决方案,祝你好运!

答案 1 :(得分:1)

获取私人字段名称

<var name="index" value="1"/>
<for param="XXX">
....
<loadproperties prefix="${index}">
    <zipentry zipfile="@{XXXX}" name="YYYY"/>
        <filterchain>
            <linecontains>
                <contains value="ZZZZZ"/>
            </linecontains>
        </filterchain>
</loadproperties>
<echo message="${${index}.ZZZZZ}"/>
....
<math result="index" operand1="${index}" operation="+" operand2="1" datatype="int" />

这里也有一些小问题

require Rails.root.join("lib/tasks/importer").to_s

如果您更改声明的顺序,那么它可能会给您带来麻烦,永远不会被折射

我建议使用

use foo.getDeclaredFields(); instead of foo.getFields();