【问题标题】:Java and genericsJava 和泛型
【发布时间】:2012-02-01 07:49:59
【问题描述】:

我是 Java 新手,如果我完全搞错了,敬请见谅。

我正在尝试编写一个通用的(在英语意义上!)数据访问类。 例如,我目前有:

public class DA<T> {
public static Dao getAccountDao() throws NamingException, SQLException {
    Context ctx = new InitialContext();
    DataSource dataSource = (DataSource)ctx.lookup("java:comp/env/jdbc/test");
    ConnectionSource connectionSource = new DataSourceConnectionSource(dataSource, new MysqlDatabaseType());            
    Dao<Account, Integer> accountDao =  DaoManager.createDao(connectionSource, Account.class);
    return accountDao;
}
}

我可以这样称呼它:

Dao<Account, Integer> accountDao = DA.getAccountDao();

但我需要为每个 Dao/模型提供一个版本。所以我正在尝试制作一些可以称为:

Dao<SomeClass, Integer> someClassDao = DA.getDao(SomeClass);

这可能吗?

我尝试过类似的方法:

public class DA {
public static Dao getDao(<T>) throws NamingException, SQLException {
    Context ctx = new InitialContext();
    DataSource dataSource = (DataSource)ctx.lookup("java:comp/env/jdbc/test");
    ConnectionSource connectionSource = new DataSourceConnectionSource(dataSource, new MysqlDatabaseType());            
    Dao<T, Integer> accountDao =  DaoManager.createDao(connectionSource, T.class);
    return accountDao;
}

}

但 Netbeans 给出错误:illegal start of type

我的大脑正在与泛型作斗争,这是他们能做到的吗?!

编辑:在下面帖子的帮助下,我必须:

public class DA<T> {
public static Dao<T, Integer> getDao(T daoType) throws NamingException, SQLException {
    Dao<T, Integer> accountDao =  DaoManager.createDao(T.class);
    return accountDao;
}

}

这会产生两个错误: non-static type variable T cannot be referenced from a static context 如果我删除 static 关键字,我会得到: cannot select from a type variable 我需要阅读泛型和静态如何协同工作,但第二个看起来像是擦除的结果(http://www.coderanch.com/t/386358/java/java/Converting-type-parameters-class),所以不确定是否可行。

前面应该提到,Dao 的东西使用了一个叫做 ORMLite 的 ORM 库,所以 createDao 等不是我的代码。

【问题讨论】:

  • 我需要实现与您类似的目的,并且与您走相同的路线。但是您正在使用的 ORMLite 库提出了一种不同的解决方法。此评论不能回答您的问题,但可能会帮助您以更好的方式实现整体目标。请参阅此SO question for more details。希望对您有所帮助。

标签: java generics


【解决方案1】:

要访问您所说的T.class,您必须将类对象传递给方法:

class Account {}

class Dao<TEntity, TId> {}

class DA {
    // your DaoManager.createDao() will also need a similar signature
    public static <TEntity> Dao<TEntity, Integer> getDao(Class<TEntity> daoType) {
        /// yadda blah, create DAO as appropriate
        return new Dao<TEntity, Integer>();
    }
}

public class Test {
    public static void main(String[] args) {
        // Pass the class literal for what you want TEntity to be as a parameter
        Dao<Account, Integer> dao = DA.getDao(Account.class);
    }
}

【讨论】:

  • 为什么会这样?编译器知道 T 是什么类型。为什么要传递另一个参数?在 C# 中,您可以使用 typeof(T) 并且不需要那个额外的无用参数。
  • @GregEnnis Java 和 C# 的泛型实现之间绝对根本的区别在于,在 Java 中,typeof(T) 不存在。为了向后兼容,Java 故意在执行类型擦除后为泛型方法生成代码。编译器只知道TgetDao() 的调用站点上代表什么——此信息不会以任何方式传播到该方法的调用中。
  • @GregEnnis C# 在这种特定情况下的不同之处在于它以某种方式自动将 typeof(T)runtime 值从调用者传播到被调用者。 (它可以通过自动向方法插入Type 参数来实现这一点,或者它可以通过为类型/方法的每个需要的实例化或完全其他的东西生成不同的代码来实现。机制并不重要。)然而,这极大地破坏了兼容性——“非泛型”代码不能调用泛型 API。
  • @GregEnnis 作为这种不兼容性的一个例子:在 Java 中,可以“改造”java.util 集合类以使用泛型。在 C# 中这是不可能的(不提供 typeof(T) 值的旧代码无法调用可能依赖于该值可用的新代码)因此全新的 System.Collections.Generic 命名空间。有关为什么会出现问题的示例,请查看 ASP.NET Web 窗体 API,例如,ControlCollection 没有实现 IEnumerable&lt;Control&gt;;或Repeater 等。不是类型安全的。
  • 好的,谢谢您的解释。不是故意在那里触动神经:)
【解决方案2】:

您希望您的 DA 类具有两种类型(例如,&lt;Account, Integer&gt;),但在您的类声明中您只指定一种类型 &lt;T&gt;。查看一些文档和示例,例如:

http://en.wikipedia.org/wiki/Generics_in_Java#Generic_class_definitions
http://docs.oracle.com/javase/tutorial/java/generics/index.html

【讨论】:

  • 在我看来他只是希望第二个参数始终是整数。我假设它是实体主键的类型,在这种情况下,整数或长整数在这样的实用程序中硬编码是合理的。
【解决方案3】:

你必须这样做:

public class DA {
        public static <T> Dao<T,Integer> getDao(Class<T> clazz) throws NamingException, SQLException {
            Context ctx = new InitialContext();
            DataSource dataSource = (DataSource)ctx.lookup("java:comp/env/jdbc/test");
            ConnectionSource connectionSource = new DataSourceConnectionSource(dataSource, new MysqlDatabaseType());            
            Dao<T, Integer> accountDao =  DaoManager.createDao(connectionSource, clazz);
            return accountDao;
        }
    }

createDao 方法也会有这样的签名:

public static <T> Dao<T,Integer> createDao(ConnectionSource source,Class<T> clazz) {
 ...
}

【讨论】:

    【解决方案4】:

    如果需要指定采用模板参数类型的方法,则模板在返回类型之前:

    <T> void foo(T param) { ... }
    

    【讨论】:

    • “模板参数”是一个糟糕的名字。泛型和模板是完全不同的概念,它们在实现方面有一些重叠。
    • 另外,仅向方法添加类型参数不会满足 OP 的要求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多