另一种Java泛型“不兼容类型”编译错误

时间:2015-10-10 15:16:29

标签: java generics

我在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

4 个答案:

答案 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(...);
}

我希望这有帮助!