为什么java 8中的新java.util.Arrays方法没有为所有基元类型重载?
我正在回顾Java8的API更改,我注意到为什么java 8中的新java.util.Arrays方法没有为所有基元类型重载?,java,arrays,java-8,Java,Arrays,Java 8,我正在回顾Java8的API更改,我注意到Java.util.Arrays中的新方法并不是对所有原语都重载。我注意到的方法有: 目前,这些新方法只处理int、long和double原语 int、long和double可能是使用最广泛的原语,因此,如果他们必须限制API,他们会选择这三种原语,但为什么他们必须限制API?以解决整个问题,而不仅仅是这个特定场景,我想我们都想知道 为什么Java8中存在接口污染 例如,在像C#这样的语言中,有一组预定义的函数类型,它们接受任意数量的带有可选
Java.util.Arrays
中的新方法并不是对所有原语都重载。我注意到的方法有:
int
、long
和double
原语
int
、long
和double
可能是使用最广泛的原语,因此,如果他们必须限制API,他们会选择这三种原语,但为什么他们必须限制API?以解决整个问题,而不仅仅是这个特定场景,我想我们都想知道
为什么Java8中存在接口污染
例如,在像C#这样的语言中,有一组预定义的函数类型,它们接受任意数量的带有可选返回类型的参数(并且每个参数最多有16个不同类型的参数T1
,T2
,T3
,…,T16
),但在JDK 8中,我们有一组不同的函数接口,具有不同的名称和不同的方法名称,其抽象方法表示已知函数的子集(即空、一元、二元、三元等)。然后,我们有大量处理基本类型的案例,甚至还有其他场景导致更多功能接口的爆炸
类型擦除问题
因此,在某种程度上,两种语言都受到某种形式的接口污染(或C#中的委托污染)。唯一的区别是,在C#中,它们都有相同的名称。不幸的是,在Java中,Function
和Function
或Function
之间没有区别,因此显然,我们不能简单地以相同的方式命名它们,我们必须为所有可能的函数组合类型想出创造性的名称。有关这方面的进一步参考,请参阅Brian Goetz的
不要认为专家组没有解决这个问题。用Brian Goetz在《金融时报》中的话说:
[…]作为一个单独的例子,让我们以函数类型为例。兰姆达酒店
Devxx提供的strawman具有函数类型。我坚持要我们搬走
他们,这使我不受欢迎。但我反对函数类型
不是我不喜欢函数类型——我喜欢函数类型--
但该函数类型与现有的
Java类型系统,擦除。擦除的函数类型是最糟糕的
两个世界。所以我们从设计中删除了这个
但我不愿意说“Java永远不会有函数类型”
(尽管我知道Java可能永远不会有函数类型)
相信为了获得函数类型,我们必须首先处理
删除。这可能,也可能不可能。但是在一个
具体化的结构类型、功能类型开始变得更多
意义[…]
这种方法的一个优点是,我们可以定义自己的接口类型,方法可以接受任意多的参数,我们可以使用它们创建lambda表达式和方法引用。换句话说,我们有能力用更多新的功能接口污染世界。此外,我们甚至可以为JDK早期版本中的接口或我们自己定义SAM类型的API早期版本创建lambda表达式。因此现在我们有能力使用Runnable
和Callable
作为功能接口
然而,这些接口变得更难记忆,因为它们都有不同的名称和方法
尽管如此,我还是想知道为什么他们没有像Scala那样解决这个问题,定义像Function0
,Function1
,Function2
,…,FunctionN
这样的接口。也许,我能提出的唯一反对意见是,他们希望最大限度地利用前面提到的在早期版本的API中为接口定义lambda表达式的可能性
缺少值类型问题
很明显,类型擦除是其中的一个驱动力。但是,如果您想知道为什么我们还需要所有这些具有类似名称和方法签名的附加函数接口,它们唯一的区别是使用了基元类型,那么让我提醒您,在Java中,我们也喜欢像C#这样的语言中的函数接口。这意味着泛型类中使用的泛型类型只能是引用类型,而不能是基元类型
换句话说,我们不能这样做:
List<int> numbers = asList(1,2,3,4,5);
为了做到:
Writer out = new StringWriter();
Consumer<String> printer = exceptionWrappingBlock(s -> out.write(s));
Writer out=new StringWriter();
消费者打印机=例外包装锁->输出写入;
也许,在未来,当我们得到和具体化时,我们将能够摆脱(或者至少不再需要使用)这些多接口中的一些
总之,我们可以看到专家组在几个设计问题上苦苦挣扎。保持向后兼容性的需要、要求或约束使事情变得困难,然后我们还有其他重要条件,如缺少值类型、类型擦除和检查异常。如果Java有第一个而缺少另外两个,那么JDK 8的设计可能会有所不同。因此,我们都必须明白,这些都是有很多权衡的困难问题,EG必须在某个地方划一条线并做出决定。这与“为什么
{Boolean,Byte,Char,Float,Short}UnaryOperator
在java.util.function
中没有接口”的问题不同吗?在没有对操作符输出进行拆箱和空检查的情况下,需要这些额外的重载才能工作。在我看来,这是一个细微的差别。决定是否支持原语,而不是支持某些原语,但不是全部,在以前,原语是完全支持的
Writer out = new StringWriter();
Consumer<String> printer = s -> out.write(s); //oops! compiler error
interface IOConsumer<T> {
void accept(T t) throws IOException;
}
static<T> Consumer<T> exceptionWrappingBlock(IOConsumer<T> b) {
return e -> {
try { b.accept(e); }
catch (Exception ex) { throw new RuntimeException(ex); }
};
}
Writer out = new StringWriter();
Consumer<String> printer = exceptionWrappingBlock(s -> out.write(s));