您的位置:首页 > 编程语言 > Java开发

Scala - Combining Scala and Java

2013-07-07 00:00 169 查看
Scala code is highly compatible with Java, most of time you can combine the languages worrying very much. The chapter will describe two aspects of combining Java and Scala. Firstly, it discussess how Scala is translated to Java which is important if you call Scala code from Java and secondly, it discusss the use of Java annotation in Scala, an important feature if you wan to to use Scala with existing Java framework.

Using Scala from Java

General rule

Scala feature maps dierectly onto the equivalent java features, scala classes, methods, strings, exception, for examples, are all compiled to te same Java bytes as their Java counterparts. Scala trait does not have equivalent in java, Scala generic is different from Java one.

Value types

a value type like Int can be transalted in two different ways to Java, whenever possible, 1. to java int to get better performance, 2. to Wrapper classes java.lang.Integer when it is has to be wrapped to inside a Java object .

Singleton object

there is noting like the signleton object in Java, and how does the compiler translate it to java code.

Given a simple example, suppose that you have the followig code.

object App {
def main(args : Array[String]) {
println("hello, world!")
}
}
and if you run some javap command.
// in scala 2.9.1. if you have just the object app, this is what you get the from the javap

javap app.class
Compiled from "31_1_UsingScalaFromJava.scala"
public final class App {
public static final void main(java.lang.String[]);
}

javap app$.class
Compiled from "31_1_UsingScalaFromJava.scala"
public final class App$ implements scala.ScalaObject {
public static final App$ MODULE$;
public static {};
public void main(java.lang.String[]);
}
and if you have a App class, then you compile them to examine the byte code you will get.
class App
{

}
and you run the javap code ,you will get.
// and if you have a App class above, with javap

javap App.class
Compiled from "31_1_UsingScalaFromJava.scala"
public class App implements scala.ScalaObject {
public static final void main(java.lang.String[]);
public App();
}

javap App$.class
Compiled from "31_1_UsingScalaFromJava.scala"
public final class App$ implements scala.ScalaObject {
public static final App$ MODULE$;
public static {};
public void main(java.lang.String[]);
}


Trait as Interfaces

if the trait has no members, then it will be translated to java interface, while if otherwise.

Annotations

we have some standard annotation, and we will introduce on how them apply on the scala code.

Additional effects from standard annotations

several annotation cause the compilter to emit extra information when targeting Java platform. It first process it according to the general Scala rules, and then it does something extra for it

Deprecation

@deprecation

Volatile fields

@volatile field , scala has the same semantic towards the java semantics

@serializable: @SerialVersionUID(1234L)

An example on the SerialVersionUID.

@serializable
@SerialVersionUID(1234L)
class SeriazableClass
{}


when it is compiled, you will see that from the javap command.
// when compiled
//    the @SerialVersionUID(1234L) will be converted to the following java Field definition
/*javap SeriazableClass.class
Compiled from "31_2_Annotations.scala"
public class SeriazableClass implements scala.ScalaObject,scala.Serializable {
public static final long serialVersionUID;
public static {};
public SeriazableClass();
}*/
Exception throw
Scala does not check for exception , unlike the Java language, however, all Scala methods are translated to Java methods that declare no throws exceptions. to interface with Java, you might want to declare the method throw exceptions .. here is what you have .

and how do you instruct java system that you code may throws exceptions that the java code can add handler tro those exceptions.

import java.io._
class Reader (fname : String) {
private val in = new BufferedReader(new FileReader(fname))

@throws (classOf[IOException])
def read = in.read()
}
and if you check the compiled bytecode.
// you will have this on the compiled code.
//
/*javap Reader.class
Compiled from "31_2_Annotations.scala"
public class Reader implements scala.ScalaObject {
public int read() throws java.io.IOException;
public Reader(java.lang.String);
}*/


java annotations

as an example of other java annotation (other than those standard annotation such as @deprecation which is an instruct to compiler), let's take the Junit's @Test annotation for example.

// Java annotations
//
import org.junit.Test
import org.junit.Assert.assertEquals

class  SetTest {
@Test
def testMultiAdd {
val set = Set() + 1 + 2 + 1 + 2 + 3
assertEquals(3, set.size)
}
}
Compile and run it, you get the following.
scalac -cp junit-4.11.jar;.;hamcrest-core-1.3.jar SetTest.scala
scala -cp junit-4.11.jar;.;hamcrest-core-1.3.jar SetTest
when run it

scala -cp junit-4.11.jar;.;hamcrest-core-1.3.jar org.junit.runner.JUnitCore SetTest
JUnit version 4.11
.
Time: 0.366

OK (1 test)


writing you own annotations

now we are going to create our own annotations, like the below.

// file
//  Ignore.java
// description:
//  show you how you can create a Java annotation

import java.lang.annotation.*;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Ignore { }
and we write the following scala code.
// suppose we have the Ignore annotation declare in Java

object Tests {
@Ignore
def testData = List(0, 1, 1, 5, -5)

def test1 {
assert(testData == testData.head :: testData.tail)
}

def test2 {
assert(testData.contains(testData.head))
}
}
and we write a function that finding the Tests.
object FindTests {
def main(args : Array[String]) {
for {
method <- Tests.getClass.getMethods
if method.getName.startsWith("test")
if method.getAnnotation(classOf[Ignore]) == null
} {
println("found a test method : " + method)
}
}
}
/*$ scala FindTests
found a test method : public void Tests$.test1()
found a test method : public void Tests$.test2()*/

A note that we may use to build the code is like this:

$javac  Ignore.java
$scalac Tests.scala
$scalac FindTests.scala
$scala FindTests


Existential Types

foreword
for java types, you have Iterator<Component> in Java and Iterator<Component> in Scala, but what for the type Iterator<?> or Iterator<? extends Component> or even raw type Iterator.

We have the existential type. as follow.

// existential type are fully supported in Scala,
type forSome { declaration }
the {declaration} part is a list of abstract vals and types. the interpretation is that the declared variable and types exist but are unknown, just like abstract members of a class.

Java Iterator<?> would be written in Scala as :

type forSome { type T}

Java Iterator<? extends Component> would be written in Scala as :

type forSome { type T <: Component>}
placeholder syntax. it means the same as the type forSome { declaration } (with the type forSome { type T}

Iterator[_]


similarily, you you can write the following placeholder syntax as follow.

Iterator[_ <: Component]
for the declaration as follow for the code is as follow. "type forSome { type T <: Component>}"

to test the placeholder and the existential type, we write the following java code.

import java.util.Collection;
import java.util.Vector;

// FIle
//  Wild.java
// Description:
//  this show you a java class with Wildcard

public class Wild {
public Collection<?>  contents() {
Collection<String> stuff = new Vector<String>();
stuff.add("a");
stuff.add("b");
stuff.add("see");
return stuff;
}
}
and with this, we can test with the following scala code.

// run it with the following commands
// scala
// :cp .
// val contents = (new Wild).contents
val contents = (new Wild).contents

contents.size()
this is a no-brainer. however, let's see a more convoluted example as follow.

// a more complicated examples is as follow.
import scala.collection.mutable.Set
val iter  = (new Wild).contents.iterator
val set = Set.empty[???] //  quandry here , what we fill in here
while (iter.hasMore)
set += iter.next()
as above, what value we provide in the ??? place? there are two solutions to attack this problem.

When apssing an existential type into a method, move type parameter from the forSome to type parameter of the method, Inside the body of the method. Inside the body of the method, you can use the type parameter to refer to the types that were in the forSome clause.

Instead of returning an existential type from a method, return an object that has abstract for each of the tyeps in the forSome clause.

import scala.collection.mutable.Set
import java.util.Collection
abstract class SetAndType {
type Elem
val set : Set[Elem]
}

def javaSet2ScalaSet[T](jset : Collection[T]): SetAndType = {
val sset = Set.empty[T]

val iter = jset.iterator

while (iter.hasNext)
sset += iter.next()

return new SetAndType {
type Elem = T // not val Elem = T
val set = sset
}
}
so in general, when programming against Scala, prefer abstract member instead of existence type.

using synchronized

for compatibility's sake, Scala provides access to Java's concurrency primitives, the wait, notify, and notifyAll methods can be called in Scala, and they have the same meaning as in Java, Scala doesn't technically have a synchronized keyword, but it includes a predefined synchronized method that can be called as follow.

var counter = 0
synchronized {
// One thread in here at a time
counter = counter + 1
}


Compiling Scala and Java together

scalac has support to compile against Java Source code as well as Java calss. scalac compiler won't compile those source code, but it will scan them to see what they contains. later you can compile those java code with those Scala source code.

scalac -d bin InventoryAnalysis.scala InventoryItem.java Inventory.java
javac -cp bin -d bin Inventory.java InventoryItem.java InventoryManagement.java
scala -cp bin InventoryManagement
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  scala