簡體   English   中英

另一個Java泛型“不兼容類型”編譯錯誤

[英]Yet another Java generics “incompatible types” compilation error

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

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;

作為構造函數參數傳遞給DB類。

作為不相關的說明,您可能希望約束T參數,因為您知道只有Connection的子類型有效,並且遵守Java命名約定(方法的lowerCamelCase)。

這是整個解決方案:

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);

您將conn定義為T類型,而不是像以前那樣定義為Connection 因此,從技術上講,T(以及conn)可以是任何對象,而這不是您的連接方法返回的對象。

如果希望它是通用的,則需要將其定義為<T extends Connecion>. 這樣,它將匹配返回的類型

在您的代碼中,模板/通用類型T可以是任何東西。 允許某人創建此類的實例,例如new db<Integer>(5) ,在這種情況下,Connect返回的類型將不兼容。 編譯器會看到這種可能性,因此會給您一個編譯時錯誤。

您打算使此類通用嗎? 您期望Connect方法返回什么其他類型?

同樣,通常在Java中,應該使用大寫的類名和小寫的方法名。 偏離典型的編碼約定時,代碼需要花費更長的時間來消化。

您沒有必要在向我們展示的代碼中使用泛型,因為正如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(...);
}

我希望這有幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM