为什么在Java中8个新java.util.Arrays中的方法不超载的所有原始类型? [英] Why are new java.util.Arrays methods in Java 8 not overloaded for all the primitive types?

查看:138
本文介绍了为什么在Java中8个新java.util.Arrays中的方法不超载的所有原始类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我检讨的Java 8 API的变化,我注意到,在 java.util.Arrays中的新方法不超载对所有原语。我注意到的方法是:

I'm reviewing the API changes for Java 8 and I noticed that the new methods in java.util.Arrays are not overloaded for all primitives. The methods I noticed are:

  • parallelSetAll
  • parallelPrefix
  • spliterator
  • stream

目前这些新的方法只能处理 INT 双击原语。

Currently these new methods only handle int, long, and double primitives.

INT 双击可能最广​​泛使用的原语是很有意义的,如果他们不得不限制他们会选择那些三个API,但是为什么他们要限制API?

int, long, and double are probably the most widely used primitives so it makes sense that if they had to limit the API that they would choose those three, but why did they have to limit the API?

推荐答案

要解决的问题作为一个整体,而不仅仅是这个特殊的情况下,我想大家都想知道....

To address the questions as whole, and not just this particular scenario, I think we all want to know....

例如,如C#语言,还有一套predefined函数类型接受任何数量的参数有一个可选的返回类型(的 Func键和的动作的每一个上升到不同类型的16个参数 T1 T2 T3 ... T16 ),但在JDK 8我们所拥有的是一套不同的功能接口,与的不同的的名称和不同的方法的名称,而其抽象方法重新present一个子集,众所周知的功能arities (即零元,一元,二元,三元等)。然后,我们必须处理的原始类型的案件发生爆炸,甚至有造成更多的功能接口的爆炸其他场景。

For instance, in a language like C#, there is a set of predefined function types accepting any number of arguments with an optional return type (Func and Action each one going up to 16 parameters of different types T1, T2, T3, ..., T16), but in the JDK 8 what we have is a set of different functional interfaces, with different names and different method names, and whose abstract methods represent a subset of well known function arities (i.e. nullary, unary, binary, ternary, etc). And then we have an explosion of cases dealing with primitive types, and there are even other scenarios causing explosion of more functional interfaces.

所以,在某种程度上,这两种语言某种形式的接口污染(或C#委托污染)之苦。唯一的区别是,在C#它们都具有相同的名称。在Java中,遗憾的是,由于类型擦除,有函数℃之间没有差异; T1,T2> 功能< T1,T2,T3> 功能< T1,T2,T3,...... Tn的> 左右,显然,我们不能简单地命名他们都以同样的方式,我们必须拿出所有可能的创意的名字类型的功能组合。

So, in a way, both languages suffer from some form of interface pollution (or delegate pollution in C#). The only difference is that in C# they all have the same name. In Java, unfortunately, due to type erasure, there is no difference between Function<T1,T2> and Function<T1,T2,T3> or Function<T1,T2,T3,...Tn>, so evidently, we couldn't simply name them all the same way and we had to come up with creative names for all possible types of function combinations.

别以为专家组没有这个问题作斗争。在作者Brian Goetz在拉姆达邮件列表的话:

Don't think the expert group did not struggle with this problem. In the words of Brian Goetz in the lambda mailing list:

[...]作为一个例子,让我们函数类型。拉姆达
  在devoxx提供了稻草人函数类型。我坚持我们删除
  他们,这让我不受欢迎。但我反对函数类型
  并不是说我不喜欢函数类型 - 我爱函数类型 -
  但此功能与类型的现有方面转战严重
  Java类型系统,删除。擦除功能类型是最坏的
  两全其美。因此,我们删除这个从设计。

[...] As a single example, let's take function types. The lambda strawman offered at devoxx had function types. I insisted we remove them, and this made me unpopular. But my objection to function types was not that I don't like function types -- I love function types -- but that function types fought badly with an existing aspect of the Java type system, erasure. Erased function types are the worst of both worlds. So we removed this from the design.

但我不愿多说的Java永远不会有函数类型
  (虽然我承认,Java的可能永远不会有功能类型。)我
  相信,为了得到函数类型,我们必须先处理
  与擦除。即可以,或者可以是不可能的。但在世界
  具体化的结构类型,函数类型开始做了很多
  感觉[...]

But I am unwilling to say "Java never will have function types" (though I recognize that Java may never have function types.) I believe that in order to get to function types, we have to first deal with erasure. That may, or may not be possible. But in a world of reified structural types, function types start to make a lot more sense [...]

该方法的优点是,我们可以定义我们自己的接口类型与方法接受尽可能多的论据,我们想,我们可以使用它们来创建拉姆达前pressions和方法的引用,因为我们认为合适的。换句话说,我们的污染世界的力量的使用还甚至更多的新功能接口。此外,我们甚至可以在早期版本的JDK接口或更早版本的我们自己的API定义的SAM类型,如这些创造拉姆达前pressions。所以现在我们必须使用的Runnable 可赎回的功能接口。

An advantage of this approach is that we can define our own interface types with methods accepting as many arguments as we would like, and we could use them to create lambda expressions and method references as we see fit. In other words, we have the power to pollute the world with yet even more new functional interfaces. Also we can create lambda expressions even for interfaces in earlier versions of the JDK or for earlier versions of our own APIs that defined SAM types like these. And so now we have the power to use Runnable and Callable as functional interfaces.

然而,这些界面变得更加难以记忆,因为它们都具有不同的名称和方法。

However, these interfaces become more difficult to memorize since they all have different names and methods.

不过,我想知道那些他们为什么没有解决的问题在Scala中,定义诸如 Function0 功能1 功能2 ... 功能N 。或许,我可以拿出对抗,唯一的理由是,他们希望最大限度地定义在早期版本的API的接口拉姆达前pressions的可能性,前面提到的。

Still, I am one of those wondering why they didn't solve the problem as in Scala, defining interfaces like Function0, Function1, Function2, ..., FunctionN. Perhaps, the only argument I can come up with against that is that they wanted to maximize the possibilities of defining lambda expressions for interfaces in earlier versions of the APIs as mentioned before.

所以,看样子类型擦除是一个在这里推动力。但如果你是那些想知道的一个,为什么我们还需要具有类似名称和方法签名,其唯一的区别是使用原始类型的所有这些额外的功能接口,然后让我提醒你,在Java中,我们 缺乏值类型的像那些在如C#语言。这意味着,在我们的通用类使用的通用的类型只能是参考的类型,而不是原始类型。

So, evidently type erasure is one driving force here. But if you are one of those wondering why we also need all these additional functional interfaces with similar names and method signatures and whose only difference is the use of a primitive type, then let me remind you that in Java we also lack of value types like those in a language like C#. This means that the generic types used in our generic classes can only be reference types, and not primitive types.

在换句话说,我们不能做到这一点:

In other words, we can't do this:

List<int> numbers = asList(1,2,3,4,5);

但我们可以做到这一点:

But we can indeed do this:

List<Integer> numbers = asList(1,2,3,4,5);

第二个例子,虽然招致了包裹的物体来回/到基本类型的装箱和拆箱的成本。这可以成为处理的原始值的集合操作真贵。因此,专家小组决定建立这个的接口爆炸的应对不同的场景。为了使事情变得不那么糟糕,他们决定只用三种基本类型的处理:的int,long和double

The second example, though, incurs in the cost of boxing and unboxing of the wrapped objects back and forth from/to primitive types. This can become really expensive in operations dealing with collections of primitive values. So, the expert group decided to create this explosion of interfaces to deal with the different scenarios. To make things "less worse" they decided to only deal with three basic types: int, long and double.

引用作者Brian Goetz的话在拉姆达邮件列表

Quoting the words of Brian Goetz in the lambda mailing list:

[...]更普遍的:背后有专门的经营理念
  原始流(例如,IntStream)充满了讨厌的权衡。
  在一方面,它的很多丑陋的code复制,界面
  污染等。另一方面,在盒装OPS任何一种算术
  太烂了,又没有什么故事,减少了整数将是可怕的。
  因此,我们正处在一个艰难的角落,而我们正在努力不使情况变得更糟。

[...] More generally: the philosophy behind having specialized primitive streams (e.g., IntStream) is fraught with nasty tradeoffs. On the one hand, it's lots of ugly code duplication, interface pollution, etc. On the other hand, any kind of arithmetic on boxed ops sucks, and having no story for reducing over ints would be terrible. So we're in a tough corner, and we're trying to not make it worse.

招数不使情况变得更糟#1:我们没有做所有八个
  基本类型。我们正在做的int,long和double;所有其他
  可以通过这些模拟。可以说,我们可以摆脱整型过,但
  我们不认为大多数Java开发人员都准备好了。是的,有
  将是性格电话,得到的答复是坚持在一个int。
  (每个专业化预计〜100K到JRE的足迹。)

Trick #1 for not making it worse is: we're not doing all eight primitive types. We're doing int, long, and double; all the others could be simulated by these. Arguably we could get rid of int too, but we don't think most Java developers are ready for that. Yes, there will be calls for Character, and the answer is "stick it in an int." (Each specialization is projected to ~100K to the JRE footprint.)

诀窍#2:我们正在使用原始数据流揭露的事情是
  在原始的领域做得最好的(排序,还原),但不尝试
  重复一切你可以在盒装域做的。例如,
  没有IntStream.into(),如阿列克谢指出。 (如果有,
  下一个问题(S)将是哪里是IntCollection?IntArrayList?
  IntConcurrentSkipListMap?)的意图是许多河流开始可能
  引用流和最终成为原始数据流,而不是相反。
  没关系,并且减少了所需的转换次数(例如,无
  地图为int的过载 - &GT; T,不为INT函数的专业化
   - &GT; T等)[...]

Trick #2 is: we're using primitive streams to expose things that are best done in the primitive domain (sorting, reduction) but not trying to duplicate everything you can do in the boxed domain. For example, there's no IntStream.into(), as Aleksey points out. (If there were, the next question(s) would be "Where is IntCollection? IntArrayList? IntConcurrentSkipListMap?) The intention is many streams may start as reference streams and end up as primitive streams, but not vice versa. That's OK, and that reduces the number of conversions needed (e.g., no overload of map for int -> T, no specialization of Function for int -> T, etc.) [...]

我们可以看出,这是专家组一个艰难的决定。我想没有人会同意,这是很酷的,而我们大多数人最有可能同意这是必要的。

We can see that this was a difficult decision for the expert group. I think few would agree that this is cool, and most of us would most likely agree it was necessary.

有是第三驱动力可能使事情更糟的,它是Java支持两种类型的例外的事实:选中和未选中。编译器要求我们处理或显式声明检查异常,但它需要为没有选中的。因此,这造成了一个有趣的问题,因为大部分功能接口的方法签名没有声明抛出任何异常。所以,举例来说,这是不可能的:

There was a third driving force that could have made things even worse, and it is the fact that Java supports two type of exceptions: checked and unchecked. The compiler requires that we handle or explicitly declare checked exceptions, but it requires nothing for unchecked ones. So, this creates an interesting problem, because the method signatures of most of the functional interfaces do not declare to throw any exceptions. So, for instance, this is not possible:

Writer out = new StringWriter();
Consumer<String> printer = s -> out.write(s); //oops! compiler error

它不能这样做,因为操作抛出一个checked异常(即 IOException异常),但的签名消费者方法不声明它抛出的任何异常都没有。所以,对这个问题的唯一解决方案将是创造更多的接口,宣布一些例外,有些不是(或拿出在语言水平的例外透明度。同样,把事情少雪上加霜专家组决定什么也不做,在这种情况下。

It cannot be done because the write operation throws a checked exception (i.e. IOException) but the signature of the Consumer method does not declare it throws any exception at all. So, the only solution to this problem would have been to create even more interfaces, some declaring exceptions and some not (or come up with yet another mechanism at the language level for exception transparency. Again, to make things "less worse" the expert group decided to do nothing in this case.

在作者Brian Goetz在拉姆达邮件列表的话

In the words of Brian Goetz in the lambda mailing list:

[...]是的,你必须提供自己的特殊的地对空导弹。但是之后
  拉姆达转换将正常工作和他们在一起。

[...] Yes, you'd have to provide your own exceptional SAMs. But then lambda conversion would work fine with them.

在EG讨论额外的语言和库支持这一
  的问题,并最终认为,这是一个糟糕的成本/效益
  权衡。

The EG discussed additional language and library support for this problem, and in the end felt that this was a bad cost/benefit tradeoff.

基于库的解决方案,导致SAM类型的2倍爆炸(特殊
  相较于未),这与现有的组合爆炸冲突很严重
  对于原始专业化。

Library-based solutions cause a 2x explosion in SAM types (exceptional vs not), which interact badly with existing combinatorial explosions for primitive specialization.

可用的基于语言的解决方案是失败者从
  复杂性/价值权衡。虽然有一些另类
  我们要继续解决方案的探索 - 尽管显然不是
  8,可能不适合或者9

The available language-based solutions were losers from a complexity/value tradeoff. Though there are some alternative solutions we are going to continue to explore -- though clearly not for 8 and probably not for 9 either.

在此期间,你有工具,做你想做什么。我得到的
  您preFER我们提供最后一英里你(和,其次,你的
  请求是真的你为什么不只是给一个自动精简含蓄的请求
  对了checked异常已经),但我认为目前的状态让
  你得到你的工作完成。 [...]

In the meantime, you have the tools to do what you want. I get that you prefer we provide that last mile for you (and, secondarily, your request is really a thinly-veiled request for "why don't you just give up on checked exceptions already"), but I think the current state lets you get your job done. [...]

所以,这是由我们的开发者,手艺的甚至更接口爆炸的处理这些的情况下,逐案:

So, it's up to us, the developers, to craft yet even more interface explosions to deal with these in a case-by-case basis:

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));

也许,在未来(或许JDK 9)当我们支持值类型在Java中并具体化,我们就能摆脱(或至少不再需要再使用)有些多个接口。

Probably, in the future (maybe JDK 9) when we get Support for Value Types in Java and Reification, we will be able to get rid of (or at least no longer need to use anymore) some of these multiple interfaces.

综上所述,我们可以看到,专家组与几个设计问题挣扎。的需要,要求或约束,以保持向后兼容性使事情困难,那么我们有像缺少值类型,类型擦除等重要条件和检查的异常。要是Java第一,缺乏其他两个JDK 8的设计可能会有所不同。所以,我们都必须明白,这些都是棘手的问题,有很多的权衡和EG不得不某处画一条线,并作出决定。

In summary, we can see that the expert group struggled with several design issues. The need, requirement or constraint to keep backwards compatibility made things difficult, then we have other important conditions like the lack of value types, type erasure and checked exceptions. If Java had the first and lacked of the other two the design of JDK 8 would probably have been different. So, we all must understand that these were difficult problems with lots of tradeoffs and the EG had to draw a line somewhere and make a decisions.

这篇关于为什么在Java中8个新java.util.Arrays中的方法不超载的所有原始类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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