簡體   English   中英

Java Stream Generics Type Mismatch

[英]Java Stream Generics Type Mismatch

在操作Java 8流時,我遇到了一個錯誤,編譯器似乎“忘記”了我的泛型參數的類型。

以下代碼段創建了一個類名流,並嘗試將流映射到Class<? extends CharSequence> Class<? extends CharSequence>

public static Stream<Class<? extends CharSequence>> getClasses() {

    return Arrays.asList("java.lang.String", "java.lang.StringBuilder", "Kaboom!")
        .stream()
        .map(x -> {
            try {
                Class<?> result = Class.forName(x);

                return result == null ? null : result.asSubclass(CharSequence.class);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return null;
        })
        //.filter(x -> x != null)
        ;

}

當我取消注釋過濾器以從流中刪除空條目時,我得到編譯錯誤

類型不匹配:無法轉換為Class <capture#15-of? 將CharSequence>擴展為Class <Object>

有人可以向我解釋為什么添加過濾器會導致此錯誤嗎?

PS:這里的代碼有些隨意,很容易讓錯誤消失:在應用過濾器之前將映射流分配給臨時變量。 我感興趣的是為什么上面的代碼片段會產生編譯時錯誤。

編輯:正如@Holger所指出的那樣,這個問題並不完全與Java 8 Streams重復:為什么Collectors.toMap對於帶有通配符的泛型有不同的行為? 因為那里有問題的片段目前編譯沒有問題,而這里的片段沒有。

這是因為類型推斷:

該類型是從它的目標“猜測”的:我們知道map(任何東西)必須返回"Stream<Class<? extends CharSequence>>"因為它是函數的返回類型。 如果你鏈接返回到另一個操作,例如過濾器或地圖,我們會松開這種類型推斷(它不能“通過”鏈接)

類型推斷有其局限性,您可以找到它。

解決方案很簡單:如果你使用變量,你可以指定目標然后幫助類型推斷。

這個編譯:

public static Stream<Class<? extends CharSequence>> getClasses() {
Stream<Class<? extends CharSequence>> map1 = Arrays.asList ("java.lang.String", "java.lang.StringBuilder", "Kaboom!").stream ().map (x -> {
  try {
    Class<?> result = Class.forName (x);
    return result == null ? null : result.asSubclass(CharSequence.class);
  } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace ();
  }

  return null;
});
return map1.filter(x -> x != null);

請注意,我修改了代碼以返回始終為null,以顯示不同的類型不是來自lambda返回類型。

我們看到map1的類型受變量聲明的影響,它的目標。 如果我們返回它,它是等價的,目標是返回類型,但如果我們鏈接它:

這不編譯:

public static Stream<Class<? extends CharSequence>> getClasses () {

return Arrays.asList ("java.lang.String", "java.lang.StringBuilder", "Kaboom!").stream ().map (x -> {
  try {
    Class<?> result = Class.forName (x);
    return result == null ? null : result.asSubclass(CharSequence.class);
  } catch (Exception e) {

    e.printStackTrace ();
  }

  return null;
}).filter(x -> x != null);

第一個映射聲明沒有目標,因此默認情況下定義了impred類型: Stream<Object>

編輯

使其工作的另一種方法是使類型推斷使用Lambda返回值(而不是目標),您需要指定帶有強制轉換的返回類型。 這將編譯:

public static Stream<Class<? extends CharSequence>> getClasses2 () {

return Arrays.asList ("java.lang.String", "java.lang.StringBuilder", "Kaboom!").stream ().map (x -> {
  try {
    Class<?> result = Class.forName (x);
     return (Class<? extends CharSequence>)( result == null ? null : result.asSubclass(CharSequence.class));
  } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace ();
  }

  return (Class<? extends CharSequence>)null;
}).filter(x -> x != null);

}

請注意,這是因為操作鏈接,你可以用map(x-> x)替換.filter(x - > x!= null)你會遇到同樣的問題。

編輯:修改示例以完全匹配問題。

除了@pdem的答案 ,這也適合你:

public class Test {

    public static void main(String[] args) {
        getAsSubclasses(CharSequence.class, "java.lang.String", "java.lang.StringBuilder", "Kaboom!")
                .forEach(System.out::println);
    }

    public static <C> Stream<Class<? extends C>> getAsSubclasses(Class<C> type, String... classNames) {
        return Arrays.stream(classNames)
                .map(new ToSubclass<>(type))
                .filter(c -> c != null);
    }

    static final class ToSubclass<C> implements Function<String, Class<? extends C>> {

        final Class<C> type;

        ToSubclass(Class<C> type) {
            this.type = type;
        }

        @Override
        public Class<? extends C> apply(String s) {
            try {
                return Class.forName(s).asSubclass(type);
            } catch (Exception e) {
                return null;
            }
        }

    }

}

因為無法確定lambda函數的返回類型(或編譯器只是不嘗試這樣做)。 使用具有正確類型參數的顯式匿名Function對象可以完全消除類型推斷的問題:

public static Stream<Class<? extends CharSequence>> getClasses() {

    return Arrays.asList("java.lang.String",
                         "java.lang.StringBuilder",
                         "Kaboom!")
    .stream().map(
        new Function<String, Class<? extends CharSequence>>() {
            public Class<? extends CharSequence> apply(String name) {
                try {
                    return Class.forName(name).asSubclass(CharSequence.class);
                } catch (Exception e) {
                }
                return null;
            }
        }
    ).filter(Objects::nonNull);

}

要看,編譯器解決了lambda函數的實際返回類型,請嘗試讓Eclipse將表達式...stream().map(<your initial lambda>)分配給局部變量(按Ctrl+2 ,然后用光標L站在表達之前)。 它是Stream<Class<? extends Object>> Stream<Class<? extends Object>>返回類型由編譯器解析,而不是預期的Stream<Class<? extends CharSequence>> Stream<Class<? extends CharSequence>>

暫無
暫無

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

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