- Notifications
You must be signed in to change notification settings - Fork102
A Java 8 (and up) compatibility kit for Scala.
License
scala/scala-java8-compat
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A Java 8 compatibility kit for Scala 2.12 and 2.11.
Javadoc ishere.
If you are using Scala 2.13 or newer only, then don't use this library! Use the classes underscala.jdk
instead; they were added to the standard library in 2.13.
We do publish 2.13 and 3.0 artifacts of scala-java8-compat, but they're only intended to be used in projects which crossbuild with 2.12 and/or 2.11.
This library is community-maintained. (The Scala team at Akka provides infrastructure and oversight.)
A set ofFunctional Interfacesforscala.FunctionN
. These are designed for convenient construction of Scala functionsusing Java 8 lambda syntax.
importscala.concurrent.*;importstaticscala.compat.java8.JFunction.*;classTest {privatestaticFuture<Integer>futureExample(Future<String>future,ExecutionContextec) {returnfuture.map(func(s ->s.toUpperCase()),ec).map(func(s ->s.length()),ec);}}
A set of converters that enable interconversion between Java's standardFunctional Interfaces defined injava.util.function
and Scala'sFunction0
,Function1
, andFunction2
traits. These are intended for use when youalready have an instance of ajava.util.function
and need a Scala function,or have a Scala function and need an instance of ajava.util.function
.
The.asScala
extension method will convert ajava.util.function
to the correspondingScala function. The.asJava
extension method will convert a Scala function tothe most specific corresponding Java functional interface. If you wish to obtaina less specific functional interface, there are named methods that start withasJava
and continue with the name of the Java functional interface. For instance, themost specific interface corresponding to the Scala functionval rev = (s: String) => s.reverse
isUnaryOperator[String]
, and that is whatrev.asJava
will produce. However,asJavaFunction(rev)
will return ajava.util.function.Function[String, String]
instead.
TheasJava
methods can also be called conveniently from Java. There are additionalasScalaFrom
methods (e.g.asScalaFromUnaryOperator
) that will perform thefunctional-interface-to-Scala-function conversion; this is primarily of use when callingfrom Java since the.asScala
extension method is more convenient in Scala.
In Scala:
importjava.util.function._importscala.compat.java8.FunctionConverters._valfoo:Int=>Boolean= i=> i>7deftestBig(ip:IntPredicate)= ip.test(9)println(testBig(foo.asJava))// Prints truevalbar=newUnaryOperator[String]{defapply(s:String)= s.reverse }List("cod","herring").map(bar.asScala)// List("doc", "gnirrih")deftestA[A](p:Predicate[A])(a:A)= p.test(a)println(testA(asJavaPredicate(foo))(4))// Prints false// println(testA(foo.asJava)(4)) <-- doesn't work// IntPredicate does not extend Predicate!
In Java:
importjava.util.function.*;importscala.compat.java8.FunctionConverters;classExample {Stringfoo(UnaryOperator<String>f) {returnf.apply("halibut"); }Stringbar(scala.Function1<String,String>f) {returnfoo(functionConverters.asJavaUnaryOperator(f)); }Stringbaz(Function<String,String>f) {returnbar(functionConverters.asScalaFromFunction(f)); }}
Conversion between Java's concurrency primitives (CompletionStage
andCompletableFuture
) and the Scala concurrency primitives (Promise
andFuture
) is enabled withscala.compat.java8.FutureConverters
singleton object:
Converters betweenscala.Option
andjava.util
classesOptional
,OptionalDouble
,OptionalInt
, andOptionalLong
.
A set of extension methods to enable explicit conversion betweenScala Option and the Java 8optional types,Optional,OptionalDouble,OptionalInt,andOptionalLong.
Note that the four Java classes have no inheritance relationship despite all encoding optional types.
importscala.compat.java8.OptionConverters._classTest {valo=Option(2.7)valoj= o.asJava// Optional[Double]valojd= o.asPrimitive// OptionalDoublevalojds= ojd.asScala// Option(2.7) again}
Scala collections gainseqStream
andparStream
as extension methods that produce a Java 8 Streamrunning sequentially or in parallel, respectively. These are automatically specialized to a primitivetype if possible, including automatically applied widening conversions. For instance,List(1,2).seqStream
produces anIntStream
, and so doesList(1.toShort, 2.toShort).parStream
. Maps additionally haveseqKeyStream
,seqValueStream
,parKeyStream
, andparValueStream
methods.
Scala collections also gainaccumulate
andstepper
methods that produce utility collections thatcan be useful when working with Java 8 Streams.accumulate
produces anAccumulator
or its primitivecounterpart (DoubleAccumulator
, etc.), which is a low-level collection designed for efficient collectionand dispatching of results to and from Streams. Unlike most collections, it can contain more thanInt.MaxValue
elements.
stepper
produces aStepper
which is a fusion ofSpliterator
andIterator
.Stepper
s underlie the Scalacollections' instances of Java 8 Streams. Steppers are intended as low-level building blocks for streams.Usually you would not create them directly or call their methods but you can implement them alongside customcollections to get better performance when streaming from these collections.
Java 8 Streams gaintoScala[Coll]
andaccumulate
methods, to make it easy to produce Scala collectionsor Accumulators, respectively, from Java 8 Streams. For instance,myStream.to[Vector]
will collect thecontents of a Stream into ascala.collection.immutable.Vector
. Note that standard sequential buildersare used for collections, so this is best done to gather the results of an expensive computation.
Finally, there is a Java class,ScalaStreamSupport
, that has a series ofstream
methods that can be used toobtain Java 8 Streams from Scala collections from within Java.
For sequential operations, Scala'siterator
almost always equals or exceeds the performance of a Java 8 stream. Thus,one should favoriterator
(and its richer set of operations) overseqStream
for general use. However, longchains of processing of primitive types can sometimes benefit from the manually specialized methods inDoubleStream
,IntStream
, andLongStream
.
Note that althoughiterator
typically has superior performance in a sequential context, the advantage is modest(usually less than 50% higher throughput foriterator
).
For parallel operations,parStream
and evenseqStream.parallel
meets or exceeds the performance of Scala parallelcollections methods (invoked with.par
). Especially for small collections, the difference can be substantial. Insome cases, when a Scala (parallel) collection is the ultimate result, Scala parallel collections can have an advantageas the collection can (in some cases) be built in parallel.
Because the wrappers are invoked based on the static type of the collection, there are also cases where parallelizationis inefficient when interfacing with Java 8 Streams (e.g. when a collection is typed asSeq[String]
so might have linearaccess likeList
, but actually is aWrappedArray[String]
(ArraySeq
on 2.13) that can be efficiently parallelized) but can be efficientwith Scala parallel collections. TheparStream
method is only available when the static type is known to be compatiblewith rapid parallel operation;seqStream
can be parallelized by using.parallel
, but may or may not be efficient.
If the operations available on Java 8 Streams are sufficient, the collection type is known statically with enough precisionto enable parStream, and anAccumulator
or non-collection type is an acceptable result, Java 8 Streams will essentiallyalways outperform the Scala parallel collections.
importscala.compat.java8.StreamConverters._objectTest {valm= collection.immutable.HashMap("fish"->2,"bird"->4)vals= m.parValueStream.sum// 6, potientially computed in parallelvalt= m.seqKeyStream.toScala[List]// List("fish", "bird")vala= m.accumulate// Accumulator[(String, Int)]valn= a.stepper.fold(0)(_+ _._1.length)+ a.parStream.count// 8 + 2 = 10valb= java.util.Arrays.stream(Array(2L,3L,4L)). accumulate// LongAccumulatorvall= b.to[List]// List(2L, 3L, 4L)}
Scala can emit Java SAMs for lambda expressions that are arguments to methods that take a Java SAM rather thana Scala Function. However, it can be convenient to restrict the SAM interface to interactions with Java code(including Java 8 Streams) rather than having it propagate throughout Scala code.
Using Java 8 Stream converters together with function converters allows one to accomplish this with only a modestamount of fuss.
Example:
importscala.compat.java8.FunctionConverters._importscala.compat.java8.StreamConverters._defmapToSortedString[A](xs:Vector[A],f:A=>String,sep:String)= xs.parStream.// Creates java.util.stream.Stream[String] map[String](f.asJava).sorted.// Maps A to String and sorts (in parallel) toArray.mkString(sep)// Back to an Array to use Scala's mkString
Note that explicit creation of a new lambda will tend to lead to improved type inference and at least equalperformance:
defmapToSortedString[A](xs:Vector[A],f:A=>String,sep:String)= xs.parStream. map[String](a=> f(a)).sorted.// Explicit lambda creates a SAM wrapper for f toArray.mkString(sep)
To convert a Scala collection to a Java 8 Stream from within Java, it usuallysuffices to callScalaStreamSupport.stream(xs)
on your collectionxs
. Ifxs
isa map, you may wish to get the keys or values alone by usingfromKeys
orfromValues
. If the collection has an underlying representation that is notefficiently parallelized (e.g.scala.collection.immutable.List
), thenfromAccumulated
(andfromAccumulatedKeys
andfromAccumulatedValues
) willfirst gather the collection into anAccumulator
and then return a stream overthat accumulator. If not running in parallel,from
is preferable (faster andless memory usage).
Note that a ScalaIterator
cannot fulfill the contract of a Java 8 Stream(because it cannot supporttrySplit
if it is called). Presently, one mustcallfromAccumulated
on theIterator
to cache it, even if the Stream willbe evaluated sequentially, or wrap it as a Java Iterator and use staticmethods inSpliterator
to wrap that as aSpliterator
and then aStream
.
Here is an example of conversion of a Scala collection within Java 8:
importscala.collection.mutable.ArrayBuffer;importscala.compat.java8.ScalaStreamSupport;publicclassStreamConvertersExample {publicintMakeAndUseArrayBuffer() {ArrayBuffer<String>ab =newArrayBuffer<String>();ab.$plus$eq("salmon");ab.$plus$eq("herring");returnScalaStreamSupport.stream(ab).mapToInt(x ->x.length()).sum();// 6+7 = 13 }}
Interconversion between Java's standardjava.time.Duration
typeand thescala.concurrent.duration.FiniteDuration
types. The JavaDuration
doesnot contain a time unit, so when converting fromFiniteDuration
the time unit usedto create it is lost.
For the opposite conversion aDuration
can potentially express a larger time span thanaFiniteDuration
, for such cases an exception is thrown.
Example of conversions from the Java type ways:
importscala.concurrent.duration._importscala.compat.java8.DurationConverters._valjavaDuration: java.time.Duration=5.seconds.toJavavalfiniteDuration:FiniteDuration= javaDuration.toScala
From Java:
importscala.compat.java8.DurationConverters;importscala.concurrent.duration.FiniteDuration;DurationConverters.toScala(Duration.of(5,ChronoUnit.SECONDS));DurationConverters.toJava(FiniteDuration.create(5,TimeUnit.SECONDS));
About
A Java 8 (and up) compatibility kit for Scala.
Resources
License
Code of conduct
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.