Java 8通用函数应该是不明确的,但是在运行时会失败 [英] Java 8 generic function should be ambiguous, but failing in runtime

查看:166
本文介绍了Java 8通用函数应该是不明确的,但是在运行时会失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将Java 7代码迁移到Java 8,所以我的代码类似于:

  package test ; 

import java.util.Arrays;
import java.util.Map;

public class Tests {
private static interface ComparableMap< K,V>扩展Map< K,V>,Comparable {}

public static void main(String [] args){
func(getString());


private static void func(Comparable ... input){
System.out.println(Arrays.toString(input));


private static void func(ComparableMap<?,?> m){
System.out.println(m);
}

private static< T extends Comparable> T getString(){
return(T)aaa;






$ b

在java 7中它正常工作,在java 8 I中'm gets:


java.lang.ClassCastException:java.lang.String不能转换为tests.Tests $ ComparableMap


如果我将一个函数定义更改为:

 私人静态< T> T getString(){
return(T)aaa;
}

编译将失败,并显示:error:


引用func不明确为什么Java 8编译器在第一种情况下不会失败? (看看bug)
是否有可能改变第二个重载函数,以便用varargs参数调用第一个函数而不改变调用本身?

解决方案

编译器错误



在第一种情况下,需要方法 getString 返回一个 Comparable 实例。编译器查找 func 方法的重载,并且它只找到一个可以接受 Comparable func(Comparable ... input) Map 不会实现该接口,所以第二个重载不适用。没有歧义。



在第二种情况下, getString 可以返回任何内容。两种超载都有效,所以存在一个模糊性。请注意,对T的演员阵容在这两种情况下都是不安全的/错误的。



美式投球



你编写的通用方法基本上告诉编译器我可以返回任何你想要实现 Comparable >的类的实例。

可以说我有以下代码:

  String str = getString(); 
Integer num = getString();

这段代码将被编译, String Integer 实现 Comparable 界面。第二行在运行时会失败:代码尝试将 String 转换为 Integer



您的第二种情况也出于我上面解释的相同原因。它承诺它可以返回任何你想要的类型。它显然无法遵守这个承诺( Runnable 这是一个随机的例子,它可能是任何东西):

 Runnable run = getString()



h2>

编译器会看到两个匹配的重载, func(Comparable ... input) func(ComparableMap<?,?> m)。它更喜欢第二个,因为可变参数方法总是最后被选中(为了兼容性的原因)。所有这些都是corect行为。

然后代码抛出ClassCastException,因为你的 getString 方法不会保留它承诺(让调用者决定返回什么样的 Comparable )。



如何解决它?



最根本的问题是你的 getString 方法做出了错误的承诺。因此,我真的不知道该代码试图完成什么。如果你能详细说明我们可以帮助你更进一步。

I'm trying to migrate Java 7 code to Java 8, so I've code similar to:

package tests;

import java.util.Arrays;
import java.util.Map;

public class Tests {
    private static interface ComparableMap<K,V> extends Map<K,V>, Comparable {}

    public static void main(String[] args) {
        func(getString());
    }

    private static void func(Comparable...input){
        System.out.println(Arrays.toString(input));
    }

    private static void func(ComparableMap <?,?> m){
        System.out.println(m);
    }

    private static <T extends Comparable> T getString(){
        return (T) "aaa";
    }
}

In java 7 it working properly, in java 8 I'm getting:

java.lang.ClassCastException: java.lang.String cannot be cast to tests.Tests$ComparableMap

And in case I'm changing one function definition to:

    private static <T> T getString(){
        return (T) "aaa";
    }

compilation will fail with: error:

reference to func is ambiguous

Why Java 8 compiler not failing in first case? (Looks bug to me) Is it possible to change 2nd overloaded function, in order to get called first function with varargs argument without changing call itself?

解决方案

The compiler error

In the first case, the method getString is required to return a Comparable instance. The compiler looks for overloads of the func method, and it finds only one that can accept a Comparable: func(Comparable ... input). Map doesn't implement that interface, so the second overload is not applicable. There is no ambiguity.

In the second case, getString can return anything. Both overloads work, so there is an ambiguity. Note though, the cast to T is unsafe/wrong in both cases.

The usafe cast

The kind of generic method you've written basically tells the compiler "I can return an instance of any class you want that implements Comparable". You can't actually keep this promise though.

Lets say I have the following code:

String str = getString();
Integer num = getString();

This code will compile, both String and Integer implement the Comparable interface. The second line will fail at runtime though: the code tries to cast a String to an Integer.

Your second case is also wrong for the same reason I explained above. It promises it can return any type you want. It obiously can't keep that promise too (Runnable here is a random example, it could be anything):

Runnable run = getString()

Your updated code

The compiler sees two possible overloads that both match, func(Comparable...input) and func(ComparableMap <?,?> m). It prefers the second one, because varargs methods are always chosen last (for compatebility reasons). All of this is corect behaviour.

The code then throws the ClassCastException because your getString method doesn't keep it promise (letting the caller decide what kind of Comparable to return).

How to fix it?

The fundamental problem is that your getString method makes a false promise. As such, I don't really know what that code tries to accomplish. If you can elaborate we may be able to help you furter.

这篇关于Java 8通用函数应该是不明确的,但是在运行时会失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆