替换泛型类型的字段

时间:2010-10-26 12:36:36

标签: java generics

我有通用结构,我需要使用各种泛型类型的属性进行搜索。

让我们考虑以下实施:

public class Person {
  private int id;
  private String name;
  // + getters & setters
}

现在我有自定义数据结构,其中一种方法就像:

public T search(T data) { ... }

这当然是无稽之谈。我在代码中真正需要的是:

Person p = structure.search(12); // person's id

Person p = structure.search("Chuck N."); // person's name

所以在pseudoJava(:)中,代码将是这样的:

public T search(T.field key)

这当然是不可能的:(但是如何处理这种情况呢?重点是:我不想强迫客户的类(如Person)实现我自己的界面或扩展我自己的有没有解决办法?

4 个答案:

答案 0 :(得分:2)

您可以通过添加Class参数使搜索签名包含type参数。举个例子:

//replace 'id' with whatever your identifier types are
public <T> T search(int id, Class<T> entityClass) { ... }

客户必须使用像

这样的方法
Person p = foo.search(123, Person.class);
NotAPerson n = foo.search(234, NotAPerson.class);

包括课程可能看起来有点难看,但是当你真正考虑事情时 - 客户是否总是知道它在寻找什么?并且search()背后的代码不需要知道要搜索的类型 - 如果您有不同类型共享的ID会怎么样?

如果您的ID不是一致的类型,则可以将签名更改为

public <T> T search(Serializable id, Class<T> entityClass) { ... }

答案 1 :(得分:2)

看起来你想要某种中间策略对象,它提取一个值,比较一个值,或许提供一个哈希码或一个比较函数。

类似的东西:

interface Matcher<T> {
    boolean matches(T obj);
}

或方法如:

    boolean matches(T obj, V value);

    V get(T obj);

    int hash(T obj);

    int compare(T a, T b);

使用当前的Java语法有点冗长(可能会因JDK 8而改变)。

你最终会得到这样的东西:

Person p = structure.search(
    new Matcher<Person>() { public boolean matches(Person person) {
        return person.getID() == 12;
    })
);

或:

Person p = structure.search(
    new Matcher<Person,Integer>() {
        public boolean matches(Person person, Integer id) {
            return person.getID() == id;
        }
    ),
    12
);

在JDK8中,可能类似于:

Person p = structure.search(
    { Person person -> person.getID() == 12 }
);

或:

Person p = structure.search(
    { Person person, Integer id -> person.getID() == id },
    12
);

或:

Person p = structure.search(
    { Person person -> person.getID() },
    12
);

或:

Person p = structure.search(
    Person#getID, 12
);

答案 2 :(得分:0)

我建议您使用Comparable接口。

Person p = structure.searchUnique(new FieldFilter(“id”,12)); //搜索ID为12的人 Person [] p = structure.searchAll(new FieldFilter(“name”,“John”)); //搜索名为John的人

此外,我们可以实现允许搜索目标类的所有可用字段的过滤器:

Person [] p = structure.searchAll(new FieldFilter(“John”)); //搜索所有约翰斯

如何实现?以下是一些提示。

首先是“结构”是什么?它是一个包装集合,可能在将来可能包含一些索引功能。这个类应该有像

这样的构造函数

结构(收集数据);

它的搜索方法迭代给定的集合并调用实现Comparable的FieldFilter的compateTo()方法:

for(T elem:collection){     if(filter.compareTo(elem)){         result.add(ELEM);     } } 返回结果;

FieldFilter应如下所示:

公共类FieldFilter实现Comparable {     private String fieldName;     私人V fieldValue;     public FieldFilter(String fieldName,V fieldValue){         this.fieldName = fieldName;         this.fieldValue = fieldValue;     }     public boolean compareTo(T elem){         Field field = elem.getClass()。getField(fieldName);         field.setAccessible(真);         return field.getValue()。equals(fieldValue);     } }

请注意,代码是直接以答案形式编写的,从未编译过,因此无法按原样使用。但我希望它能描述这个想法。

答案 3 :(得分:0)

如果要在搜索的类型和传递给search()方法的参数类型之间通过泛型强制执行映射,则必须在某处指定。如果没有至少一个用于客户端类的通用标记接口,我认为没有办法做到这一点。

这样的东西会起作用:

public interface Searchable<T> { }

public class Person implements Searchable<String> {
    // ...
}

public <T extends Searchable<K>, K> T search(K key) { 
    // ...
}

现在编译器将允许:

Person p = search("John Doe");

但不是:

Person p = search(5);

如果连标记界面都不是一个选项,我担心您最好的选择是使用基于参数类型的特定搜索方法,如:

public <T> T searchByInt(int key) { 
    // ...
}

public <T> T searchByString(String key) { 
    // ...
}

但当然这意味着无效的案例,如

Person p = searchByInt(5);

不会在编译时捕获,而是需要在运行时检查。