通用类,了解通用参数

时间:2015-12-21 21:54:44

标签: java generics

我在理解为什么eclipse抱怨时遇到了一些麻烦。 我有两个接口:

@Preload
public interface KBaseAo extends FAQ
{
    public static final String ID = "BASE_KEY";
}

public interface FAQ extends Entity {
    @Unique
    @NotNull
    String getBaseKey();
    void setBaseKey(String baseKey);

    @NotNull
    String getExcerpt();
    void setExcerpt(String excerpt);
}

实体界面:

public interface Entity extends RawEntity<Integer> {
    @AutoIncrement
    @NotNull
    @PrimaryKey("ID")
    public int getID();
 }

具体类看起来像这样:

public class FAQServiceImpl<T extends FAQ> {    
    private void save(Class<T> clazz, String ID_FIELD, String key, String excerpt) {
        T entity = (T) aoManager.create(clazz, new DBParam(ID_FIELD, key),
                new DBParam("EXCERPT", excerpt));
        entity.save();
    }

    private <T extends FAQ> void save2(Class<T> clazz, String ID_FIELD, String key, String excerpt) {
        T entity = (T) aoManager.create(clazz, new DBParam(ID_FIELD, key),new DBParam("EXCERPT", excerpt));
            entity.save();
   }
    public Map<String, String> fetchExcerptsFor(Set<String> bodies) {
         //call save2 or save here
    }
}

我正在尝试将保存称为

save(KBaseAo.class, "SomeID", "SomeStr", "SomeStr");

,它给出了编译错误

The method save(Class<T>, String, String, String) in the type FAQServiceImpl<T> is not applicable for the arguments (Class<KBaseAo>, String, String, String)

调用save2完全相同

save2(KBaseAo.class, "SomeID", "SomeStr", "SomeStr");

完美编译但会覆盖T类型。

我缺少什么,无法使save()工作?

编辑: 添加了两个保存的调用代码

2 个答案:

答案 0 :(得分:2)

您的save方法使用了类级别类型变量T。在执行此操作时,您要求编译器确保仅使用类型与实例的泛型类型完全匹配的类实例调用此方法。

例如:

FAQServiceImpl<FooFAQ> instance = new FAQServiceImpl<>();
instance.save(FooFAQ.class, "", "", "", "");  // Fine.

你不能用一些任意类来调用它,这也恰好实现FAQ,因为它与instance的类型不匹配:

FAQServiceImpl<FooFAQ> instance = new FAQServiceImpl<>();
instance.save(BarFAQ.class, "", "", "", "");  // Compiler error.

但这正是您目前在代码中所做的事情:您说FAQServiceImpl具有泛型类型T,但您想要使用特定类型KBaseAo调用该方法:

FAQServiceImpl<T> instance = new FAQServiceImpl<>();
instance.save(KBaseAo.class, "", "", "", "");  // Compiler error.   

(即使这在类中的实例方法内部进行)。

但是,save2定义了一个新的类型变量,恰好具有相同的名称T。这个新类型变量与类级变量T之间没有关系。

因此,save2的声明不要求类匹配:它只需要一些实现FAQ类。

答案 1 :(得分:0)

这种方法有一个重要的区别......

public void save(Class<T> clazz, String ID_FIELD, String key, String excerpt)

......而这一个......

public <T extends FAQ> void save2(Class<T> clazz, String ID_FIELD, String key, String excerpt)

。前者是泛型的常规方法,而后者是泛型方法,它可以属于泛型类或普通类。特别是,第二种方法中的T是方法签名中声明为<T extends FAQ>的方法,它与主机可能声明的任何类型参数T无关。类。

因此,第一个方法中的T是来自调用该方法的对象的声明中的显式或推断的T,而T第二种方法中的方法是调用方法的显式或推断方法。没有什么要求它们之间存在任何关系,因此在后者中使用不同的类型参数会更加清晰(这也会导致不影响类的同名类型参数)。

此外,您需要注意,无论TU两种类型之间是否存在任何关系,Klass<T>Klass<U>类型都不具有分配兼容性方向。这就是为什么,例如,您无法将List<String>分配给List<Object>类型的变量。

如果从类的其他方法之一调用某个方法,则编译器不知道类的类型参数T具有什么值。您提供的特定save()调用仅在TKBaseAo时有效,但编译器看不到保证T将具有该值。另一方面,save2()的调用并不关心类的类型参数的值。可以从方法参数推断出该方法自身的同名类型参数的值 - 具体来说,可以从第一个参数中推断出来。