我在stackoverflow检查了几十个线程但找不到答案。所以,让我向您展示我班级的最小化版本:
class db{
private int idx;
private Connection conn;
db(int _idx){
idx = _idx;
}
void Connect(){
conn = DriverManager.getConnection("jdbc:mysql://...connection string");
//System.out.println(conn.getClass().getName());
//^^^ prints com.mysql.jdbc.JDBC4Connection
}
public static void main(String args[]){
db d = new db(1);
d.Connect();
}
}
上面的代码汇编得很好。但是,如果我尝试将其转换为模板类,则不会。我这样做是这样的:
class db<T>{
private int idx;
private T conn;
db(int _idx){
idx = _idx;
}
T Connect(){
conn = DriverManager.getConnection("jdbc:mysql://...connection string");
//System.out.println(conn.getClass().getName());
//^^^ prints com.mysql.jdbc.JDBC4Connection
return conn;
}
public static void main(String args[]){
db<com.mysql.jdbc.JDBC4Connection> d = new db<com.mysql.jdbc.JDBC4Connection>(1);
//d.Connect();
}
}
这会导致“不兼容的类型”编译错误。我想,我所做的只是基本的 - 我检查conn
类型是com.mysql.jdbc.JDBC4Connection
,然后我将其作为模板参数提供。但由于某种原因,它不起作用。我做错了什么?
修改
这是整个错误消息:
$ javac db.java
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
db.java:34: error: incompatible types
conn = DriverManager.getConnection("jdbc:mysql://" + host, user, password);
^
required: T
found: Connection
where T is a type-variable:
T extends Object declared in class db
1 error
答案 0 :(得分:3)
该行
conn = DriverManager.getConnection("jdbc:mysql://" + host, user, password);
无法编译,因为getConnection()
的返回类型(Connection
)不知道是con
的声明类型的子类型(T
,并由构造函数的调用者指定。)
因此,您必须添加类型转换。一种方法是:
conn = (T) DriverManager.getConnection("jdbc:mysql://" + host, user, password);
在这里,编译器会警告您取消选中强制转换,这意味着强制转换的正确性不会在运行时验证,但即使连接类型错误也会成功。因此,我建议采用更明确的解决方案:
conn = tClass.cast(DriverManager.getConnection("jdbc:mysql://" + host, user, password));
在运行时检查此反射模型,但需要
Class<T> tClass;
作为构造函数参数传递给您的数据库类。
作为一个不相关的注释,您可能希望约束T参数,因为您知道只有Connection的子类型有效,并且遵守Java命名约定(lowerCamelCase for methods)。
以下是整个解决方案:
public class DB<T extends Connection> {
private final Class<T> tClass;
private T conn;
public DB(Class<T> tClass) {
this.tClass = tClass;
}
void connect(){
conn = tClass.cast(DriverManager.getConnection("jdbc:mysql://...connection string"));
}
可以像:
一样使用new DB<>(com.mysql.jdbc.JDBC4Connection.class);
答案 1 :(得分:2)
您将conn
定义为T类型,而不是像以前那样Connection
。所以tecnically,T(因此conn)可以是任何对象,这不是你的连接方法返回的。
如果您希望它是通用的,您需要将其定义为<T extends Connecion>.
这样它将匹配返回的类型
答案 2 :(得分:2)
在您的代码中,模板/泛型类型T可以是任何内容。允许某人创建此类的实例,如new db<Integer>(5)
,在这种情况下,来自Connect的返回类型将是不兼容的。编译器认为这是不可能的,因此会给你一个编译时错误。
您打算将此类设为通用的意图是什么?您可以通过Connect方法返回其他类型的其他类型吗?
此外,通常在Java中,应该使用大写的类名和小写的方法名。当代码偏离典型的编码约定时,代码需要更长的时间来消化。
答案 3 :(得分:1)
没有理由在您向我们展示的代码中使用泛型,因为正如meriton的答案中所解释的那样,您需要转换为T
。它也可能是:
class db {
private int idx;
private Connection conn;
db(int _idx){
idx = _idx;
}
Connection Connect(){
conn = DriverManager.getConnection("jdbc:mysql://...connection string");
return conn;
}
public static void main(String args[]){
db d = new db(1);
}
}
您的班级中可能还有其他方法可以证明使用泛型,但您需要提供更多详细信息供我们说明。
修改强>
但是,如果您需要泛型,那么您可以采用不涉及转换为T
的方式执行此操作。
这是抽象基类:
public abstract class Db<T extends Connection> {
private int idx;
private T t;
public Db(int _idx) {
idx = _idx;
}
public void setT(T t) {
this.t = t;
}
public abstract T connect();
}
以下是具体实现的示例:
public final class Concrete1 extends Db<JDBC4Connection> {
public Concrete1(int id) {
// I'm not sure whether the id is supposed to be the id of the type
// of Connection. If so you should just pass a specific number
// e.g. super(1) and get rid of the id parameter.
super(id);
}
@Override
public JDBC4Connection connect() {
JDBC4Connection conn = whateverYouNeedToDoToGetSuchAThingIDontKnowIveNeverHeardOfIt();
setT(conn);
return conn;
}
}
您可以为其他特定类型实现其他实现,但其想法是大多数代码都是通用的,因此可以在抽象基类中编写。
有些人认为它是一个反模式来公开扩展泛型类的类,但它们本身并不是通用的。您可能更喜欢将具体类设为私有,并提供静态工厂方法来返回它们:
public static Db<JDBC4Connection> getJDBC4Connection() {
return new Concrete1(...);
}
我希望这有帮助!