簡體   English   中英

Java泛型 - 類型推導

[英]Java generics - type deducing

考慮以下:

 public class GenericTest {
    static void print(int x) {
        System.out.println("Int: " + x);
    }
    static void print(String x) {
        System.out.println("String: " + x);
    }

    static void print(Object x) {
        System.out.println("Object: " + x);
    }

    static <T> void printWithClass(T t) {
        print(t);
    }
    public static void main(String argsp[]) {
        printWithClass("abc");
    }
}

它打印Object:abc。 為什么不打印字符串:abc?

這是因為Java類型擦除 :你的

static <T> void printWithClass(T t) {
    print(t);
}

實際上是一個語法糖

static void printWithClass(Object t) {
    print(t);
}

公平地說,“語法糖”讓編譯器做了一些非常好的和重要的檢查,但是在運行時只有printWithClass方法的一個副本,它使用java.lang.Object作為變量的類型t

如果您使用其他語言(C#,C ++模板,Ada)中的泛型,那么類型擦除將與您所知道的相反,但這就是它在封面下的工作方式。

Java支持方法覆蓋(動態類型綁定),但不支持你想要實現的東西(重載是靜態多態而不是動態)。

為了實現您希望在Java中實現的目標,您需要雙重調度。

訪客模式應該是你的朋友。

我給你寫了一個代碼示例。

public class Test {

    public static void main(String argsp[]) {
        PrintTypeImpl typeImpl = new PrintTypeImpl(new StringType(), new IntType(), new ObjectType());
        typeImpl.accept(new PrintVisitor());
    }

    static final class PrintVisitor implements TypeVisitor {
        public void visit(IntType x) {
            System.out.println("Int: ");
        }

        public void visit(StringType x) {
            System.out.println("String: ");
        }

        public void visit(ObjectType x) {
            System.out.println("Object: ");
        }
    }

    interface TypeVisitor {
        void visit(IntType i);

        void visit(StringType str);

        void visit(ObjectType obj);
    }

    interface PrintType {
        void accept(TypeVisitor visitor);
    }

    static class StringType implements PrintType {
        @Override
        public void accept(TypeVisitor visitor) {
            visitor.visit(this);
        }
    }

    static class ObjectType implements PrintType {
        @Override
        public void accept(TypeVisitor visitor) {
            visitor.visit(this);
        }
    }

    static class IntType implements PrintType {
        @Override
        public void accept(TypeVisitor visitor) {
            visitor.visit(this);
        }
    }

    static final class PrintTypeImpl implements PrintType {

        PrintType[] type;

        private PrintTypeImpl(PrintType... types) {
            type = types;
        }

        @Override
        public void accept(TypeVisitor visitor) {
            for (int i = 0; i < type.length; i++) {
                type[i].accept(visitor);
            }
        }
    }

}

它不是類型擦除,它是一個編譯問題,如果JVM存儲方法在運行時泛型,也會發生同樣的事情。 它也與類型推斷無關 - 編譯器按預期推斷<String>

問題是當編譯器為printWithClass生成代碼時,它需要一個特定的方法簽名來與print調用相關聯。 Java沒有多個調度,因此它不能在方法表中添加模糊簽名並決定在運行時調用什么。 T上唯一的上限是Object ,因此唯一匹配的方法是print(Object)

因為java泛型不是你認為它們的泛型。 當編譯通用java代碼時,實際上所有類型信息都被剝離,並且只保留基本已知類型。 在這種情況下,該類型是Object

java中的泛型實際上只是編譯器技巧,編譯器會刪除原本需要的強制轉換並引發編譯時間限制。 最后,剩下的就是基本類型,當它實際編譯成字節代碼時。

此過程稱為類型擦除 之前的問題有助於了解實際情況。

因為它只能在運行時知道,但實際上,因為java是一種編譯語言而不是腳本語言,所以它是在編譯時決定的。

java泛型允許“在提供編譯時類型安全性的同時對各種類型的對象進行操作的類型或方法”。

您當然可以嘗試以下方法:

static <T extends String> void printWithClass(T t) {
    print(t);
}

雖然它不是你所追求的,但這是不可能的,因為編譯器正在調用。

static <T> void printWithClass(T t) {
    print(t);
}

將被加入到

static void printWithClass(Object t) {
    print(t);
}

泛型由編譯器解釋,它們強制執行其他類型檢查以避免任何運行時轉換問題。 通用類型信息在運行時丟失 所以在運行時printWithClass接收的只是對象而不是String,因此也就是你的結果。

舉例說明:

public class OverloadingWithGenerics {

    static void print(Integer x) {
        System.out.println("Integer: " + x);
    }

    static void print(Double x) {
        System.out.println("Double: " + x);
    }

    static void print(Number x) {
        System.out.println("Number: " + x);
    }

    static <T extends Number> void printWithClass(T t) {
        print(t);
    }

    public static void main(String argsp[]) {
        printWithClass(new Integer(1234));
    }
}

這打印:

Number: 1234

暫無
暫無

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

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