[英]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.