簡體   English   中英

有沒有辦法判斷運行時類型是否被刪除

[英]Is there a way to tell if the runtime type was erased

這將是一個有點復雜的解釋,但我會嘗試。

假設您有一個通用 class:

static class Box<T extends Number> {

    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

}

還有一個允許以反射方式調用getValue的方法:

 // it's just an example, the real world scenario is slightly more involved

 private static final Lookup LOOKUP = MethodHandles.lookup();     

 public static <T, R> T result(String methodName, Class<T> propertyClass, R instance) {
    try {

        /* line1 */ 
        MethodHandle handle = LOOKUP.findVirtual(
              instance.getClass(), 
              methodName, 
              MethodType.methodType(propertyClass)
        );

        /* line2 */
        handle = handle.asType(handle.type()
                       .changeReturnType(Object.class)
                       .changeParameterType(0, Object.class));

        /* line3 */
        Object obj = handle.invokeExact(instance);
        return propertyClass.cast(obj);

    } catch (Throwable t) {
        throw new RuntimeException(t);
    }
}

這是做什么的

  • getValue方法創建一個MethodHandle

  • 調整MethodHandle ,以便我可以在其上調用invokeExact (否則我需要調用invoke ,這會更慢)。 但這一步完全是可選的。

  • 一旦我構建了MethodHandle ,就調用它。

現在讓我們嘗試調用它:

public static void main(String[] args) throws Throwable {
    Box<Long> box = new Box<>();
    box.setValue(42L);
    result("getValue", Long.class, box);
}

這應該有效,對吧? 嗯,沒有。 這將失敗:

 Caused by: java.lang.NoSuchMethodException: no such method: GenericTest$Box.getValue()Long/invokeVirtual

我明白為什么,因為T extends Number擦除類型是Number ,所以調用應該是:

result("getValue", Number.class, box); // not Long.class

這對我來說是顯而易見的,但對我工作場所圖書館的來電者來說卻不是,我不能責怪他們。 請注意,這是一個簡化的示例...


當他們構建Box<Long> box = new Box<>(); 對於Long類型,提供Long.class是很自然的,而不是Number.class 解決方案顯然是微不足道的,但是,我在想,如果我可以(在運行時)“看到” getValue的返回類型是泛型類型,我可以拋出一個正確的錯誤消息。 例如:

"you provided Long.class, but the generic type was erased to ..."

換句話說,如果我可以在運行時告訴我返回類型是來自getValueNumber.class並且它是一些擦除的結果,那么我在以后的決定中可能會更聰明一些。

那可能嗎?

好吧,也許你可以使用好的舊反射。 使用反射允許您通過名稱和參數類型而不是返回類型來查找方法。 然后,您可以檢查返回類型以查看調用者是否提供了正確的類型:

    Method method = instance.getClass().getMethod(methodName);
    Class<?> rtype = method.getReturnType();
    if (rtype != propertyClass) {
        throw new IllegalArgumentException("must use " + rtype + " instead of " + propertyClass);
    }
    MethodHandle handle = LOOKUP.unreflect(method);

您可能需要根據需要調整反射查找(getMethod 或 getDeclaredMethod)的方式。 您可能還需要檢查以確保匹配的方法不是抽象的或 static。 (有人會認為它不能是抽象的,因為您提供了一個實例,但可能存在我沒有想到的邊緣情況,例如單獨編譯。)您可能還需要檢查該方法是否聲明在您正在反映的相同 class 。 由於您關心性能,因此進行反射可能會太慢。 但是,如果您只關心診斷,您可以嘗試快樂的路徑,如果您獲得 NSME,請進行反射查找以獲得正確的返回類型。

暫無
暫無

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

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