Scala入門到精通——第二十八節 Scala與JAVA互操作
本節主要內容
- JAVA中呼叫Scala類
- Scala中呼叫JAVA類
- Scala型別引數與JAVA泛型互操作
- Scala與Java間的異常處理互操作
1. JAVA中呼叫Scala類
Java可以直接操作縱Scala類,如同Scala直接使用Java中的類一樣,例如:
//在Person.scala檔案中定義Scala語法的Person類
package cn.scala.xtwy.scalaToJava
class Person(val name:String,val age:Int)
//伴生物件
object Person{
def getIdentityNo()= {"test" }
}
//ScalaInJava.java檔案中定義了ScalaInJava類
//直接呼叫Scala中的Person類
package cn.scala.xtwy.scalaToJava;
public class ScalaInJava {
public static void main(String[] args) {
Person p=new Person("搖擺少年夢", 27);
System.out.println("name="+p.name()+" age="+p.age());
//伴生物件的方法當做靜態方法來使用
System.out .println(Person.getIdentityNo());
}
}
對!就是這麼簡單,Java似乎可以無縫操縱Scala語言中定義的類,在trait那一節中我們提到,如果trait中全部是抽象成員,則它與java中的interface是等同的,這時候java可以把它當作介面來使用,但如果trait中定義了具體成員,則它有著自己的內部實現,此時在java中使用的時候需要作相應的調整。我們先看下trait中全部是抽象成員的情況,例如:
//全部是抽象成員,與java的interface等同
trait MySQLDAO{
def delete(id:String):Boolean
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
//MySQLDAO位元組碼反編譯後的結果
D:\ScalaWorkspace\ScalaChapter28\bin\cn\scala\xtwy\scalaToJava>javap MySQLDAO.cl
ass
Compiled from "MySQLDAO.scala"
public interface cn.scala.xtwy.scalaToJava.MySQLDAO {
public abstract boolean delete(java.lang.String);
public abstract boolean add(java.lang.Object);
public abstract int update(java.lang.Object);
public abstract scala.collection.immutable.List<java.lang.Object> query(java.l
ang.String);
}
//java直接implement,與普通的java介面一樣
public class MySQLDAOImpl implements MySQLDAO{
@Override
public boolean delete(String id) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean add(Object o) {
// TODO Auto-generated method stub
return false;
}
@Override
public int update(Object o) {
// TODO Auto-generated method stub
return 0;
}
@Override
public List<Object> query(String id) {
// TODO Auto-generated method stub
return null;
}
}
那如果Trait中包括了具體的成員,此時又該怎麼使用呢?此時需要作特殊處理,程式碼如下:
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
trait MySQLDAO {
//具體方法
def delete(id:String):Boolean={ true}
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
//JAVA語言實現帶有具體成員方法的MySQLDAO
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
public class MySQLDAOImpl implements MySQLDAO {
@Override
public boolean delete(String id) {
//呼叫生成帶有具體delete方法實現的MySQLDAO$class
if (MySQLDAO$class.delete(this, id)) return true;
else return false;
}
@Override
public boolean add(Object o) {
return false;
}
@Override
public int update(Object o) {
return 0;
}
@Override
public List<Object> query(String id) {
return null;
}
}
用javap命令檢視帶具體成員方法的trait MySQLDAO時,其程式碼是一樣的
D:\ScalaIntellijIDEAWorkSpace\out\production\ScalaChapter28\cn\scala\xtwy\JavaRe
vokeScala>javap MySQLDAO$class.class
Compiled from "MySQLDAO.scala"
public abstract class cn.scala.xtwy.JavaRevokeScala.MySQLDAO$class {
public static boolean delete(cn.scala.xtwy.JavaRevokeScala.MySQLDAO, java.lang
.String);
public static void $init$(cn.scala.xtwy.JavaRevokeScala.MySQLDAO);
}
D:\ScalaIntellijIDEAWorkSpace\out\production\ScalaChapter28\cn\scala\xtwy\JavaRe
vokeScala>javap MySQLDAO.class
Compiled from "MySQLDAO.scala"
public abstract class cn.scala.xtwy.JavaRevokeScala.MySQLDAO$class {
public static boolean delete(cn.scala.xtwy.JavaRevokeScala.MySQLDAO, java.lang
.String);
public static void $init$(cn.scala.xtwy.JavaRevokeScala.MySQLDAO);
}
但其實並不是這樣的,經本人查閱相關資料發現,可能是scala版本原因導致的,這篇文獻中提到的跟實際情況應該是穩合的http://www.importnew.com/6188.html
這篇文章中給出了下面這樣一個trait的定義:
trait MyTrait {
def traitName:String
def upperTraitName = traitName.toUpperCase
}
它生成下面兩個位元組碼檔案MyTrait.class、MyTrait$class
[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait
Compiled from "Scalaisms.scala"
public interface com.twitter.interop.MyTrait extends scala.ScalaObject{
public abstract java.lang.String traitName();
public abstract java.lang.String upperTraitName();
}
[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait$class
Compiled from "Scalaisms.scala"
public abstract class com.twitter.interop.MyTrait$class extends java.lang.Object{
public static java.lang.String upperTraitName(com.twitter.interop.MyTrait);
public static void $init$(com.twitter.interop.MyTrait);
}
這種情況應該是跟實際情況穩合的,trait MyTrait會自動生成一個名為MyTrait的interface,MyTrait$class的抽象類。我們可以看到,該作者的scala版本是2.8.1,而我們的scala版本是2.10.4,至於為什麼出現這樣的原因,本人暫時還沒有弄清楚,但可以肯定的是,http://www.importnew.com/6188.html這篇文章講的內容跟實際是穩合的,因為前面的MySQLDAOImpl仍然是實現MySQLDAO介面方式定義的,但在重寫delete方法時採用的是
@Override
public boolean delete(String id) {
//呼叫生成帶有具體delete方法實現的MySQLDAO$class
if (MySQLDAO$class.delete(this, id)) return true;
else return false;
}
這種方式進行方法的實現,即MySQLDAO$class是個抽象類,該抽象類中包含了MySQLDAO中實現的方法。也即
trait MySQLDAO {
//具體方法
def delete(id:String):Boolean={ true}
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
最終反編譯後的程式碼應該具有以下形式:
public cn.scala.xtwy.JavaRevokeScala.MySQLDAO extends scala.ScalaObject{
public abstract boolean delete(java.lang.String);
public abstract boolean add(java.lang.Object);
public abstract int update(java.lang.Object);
public abstract scala.collection.immutable.List<java.lang.Object> query(java.l
ang.String);
}
值得注意的是在Scala IDE for Eclipse中不能實現下列程式碼的呼叫
@Override
public boolean delete(String id) {
//呼叫生成帶有具體delete方法實現的MySQLDAO$class
if (MySQLDAO$class.delete(this, id)) return true;
else return false;
}
只有在Intellij IDEA中才能正確使用,從這點上也說明了Intellij IDEA在編寫scala應用程式時更貼近實際。
2. Scala中呼叫JAVA類
Scala可以直接呼叫Java實現的任何類,只要符合scala語法就可以,不過某些方法在JAVA類中不存在,但在scala中卻存在操作更簡便的方法,例如集合的foreach方法,在java中是不存在的,但我們想用的話怎麼辦呢?這時候可以通過隱式轉換來實現,scala已經為我們考慮到實際應用場景了,例如:
import java.util.ArrayList;
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
class RevokeJavaCollections {
def getList={
val list=new ArrayList[String]()
list.add("搖擺少年夢")
list.add("學途無憂網金牌講師")
list
}
def main(args: Array[String]) {
val list=getList
//因為list是java.util.ArrayList型別,所以下這條語句編譯不會通過
list.foreach(println)
}
}
此時只要引入scala.collection.JavaConversions._包就可以了,它會我們自動地進行隱式轉換,從而可以使用scala中的一些非常方便的高階函式,如foreach方法,程式碼如下:
package cn.scala.xtwy.ScalaRevokeJava
import java.util.ArrayList;
//引入下面這條語句後便可以呼叫scala集合中的方法,如foreach,map等
import scala.collection.JavaConversions._
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
object RevokeJavaCollections{
def getList={
val list=new ArrayList[String]()
list.add("搖擺少年夢")
list.add("學途無憂網金牌講師")
list
}
def main(args: Array[String]) {
val list=getList
//現在可以呼叫scala集合中的foreach等方法了
list.foreach(println)
val list2=list.map(x=>x*2)
println(list2)
}
}
前面我們使用的是隱式轉換,我們還可以顯式地進行轉換,例如:
import java.util.ArrayList;
import scala.collection.JavaConversions._
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
object RevokeJavaCollections{
def getList={
val list=new ArrayList[String]()
list.add("搖擺少年夢")
list.add("學途無憂網金牌講師")
list
}
def main(args: Array[String]) {
val list=getList
list.foreach(println)
val list2=list.map(x=>x*2)
println(list2)
//顯式地進行轉換
val listStr=asJavaIterable(JavaListString.getListString)
for(i<- listStr)
println(i)
}
}
下面給出的是Scala集合與Java集合支援的轉換操作(取自JavaConversions原始碼):
/** A collection of implicit conversions supporting interoperability between
* Scala and Java collections.
*
* The following conversions are supported:
*{{{ //相互轉換
* scala.collection.Iterable <=> java.lang.Iterable
* scala.collection.Iterable <=> java.util.Collection
* scala.collection.Iterator <=> java.util.{ Iterator, Enumeration }
* scala.collection.mutable.Buffer <=> java.util.List
* scala.collection.mutable.Set <=> java.util.Set
* scala.collection.mutable.Map <=> java.util.{ Map, Dictionary }
* scala.collection.mutable.ConcurrentMap (deprecated since 2.10) <=> java.util.concurrent.ConcurrentMap
* scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap
*}}}
* In all cases, converting from a source type to a target type and back
* again will return the original source object, eg.
*
*{{{//源型別到目標型別轉換,再從轉換回去,得到的是相同物件
* import scala.collection.JavaConversions._
*
* val sl = new scala.collection.mutable.ListBuffer[Int]
* val jl : java.util.List[Int] = sl
* val sl2 : scala.collection.mutable.Buffer[Int] = jl
* assert(sl eq sl2)
*}}}
* In addition, the following one way conversions are provided:
*
*{{{ //只支援單向轉換的類
* scala.collection.Seq => java.util.List
* scala.collection.mutable.Seq => java.util.List
* scala.collection.Set => java.util.Set
* scala.collection.Map => java.util.Map
* java.util.Properties => scala.collection.mutable.Map[String, String]
*}}}
*
* @author Miles Sabin
* @author Martin Odersky
* @since 2.8
*/
3. Scala型別引數與JAVA泛型互操作
Java中的泛型可以直接轉換成Scala中的泛型,在前面的課程中我們已經有所涉及,例如Java中的Comparator<T>
可以直接轉換成 Scala中的Comparator[T]
使用方法完全一樣,不同的只是語法上的。下列程式碼給出了其使用方法:
package cn.scala.xtwy.JavaAndScalaGeneric
import java.util._
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
case class Person(val name:String,val age:Int)
//在Java中Comparator是這麼用的:Comparator<Person>
//而在Scala中,是這麼用的:Comparator[Person]
class PersonComparator extends Comparator[Person]{
override def compare(o1: Person, o2: Person): Int = if(o1.age>o2.age) 1 else -1
}
object ScalaUseJavaComparator extends App{
val p1=Person("搖擺少年夢",27)
val p2=Person("李四",29)
val personComparator=new PersonComparator()
if(personComparator.compare(p1,p2)>0) println(p1)
else println(p2)
}
下面的程式碼演示了Java是如何使用Scala中的泛型的:
package cn.scala.xtwy.JavaAndScalaGeneric
import scala.beans.BeanProperty
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
//Student類用泛型定義,成員變數name及age指定泛型引數
//並且用註解的方式生成JavaBean規範的getter方法
//因為是val的,所以只會生成getter方法
class Student[T,S](@BeanProperty val name:T,@BeanProperty val age:S){
}
package cn.scala.xtwy.JavaAndScalaGeneric;
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
public class JavaUseScalaGeneric {
public static void main(String[] args){
Student<String,Integer> student=new Student<String,Integer>("小李",18);
//Scala版本的getter方法
System.out.println(student.name());
//JavaBean版本的getter方法
System.out.println(student.getName());
}
}
通過上述程式碼,我們已經十分清楚了Scala中的泛型如何與Java中的泛型進行互操作了,但還有一個問題值得去考慮,那就是Java中的萬用字元的泛型如何與Scala中的泛型進行操作呢?例如:
package cn.scala.xtwy.JavaAndScalaGeneric;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
public class JavaWildcardGeneric {
//Java的萬用字元型別,要接受任何型別
public static List<?> getList(){
List<String> listStr=new ArrayList<String>();
listStr.add("搖擺少年夢");
listStr.add("學途無憂網金牌講師");
return listStr;
}
}
package cn.scala.xtwy.JavaAndScalaGeneric
import java.util.List
import scala.collection.JavaConversions._
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
class ScalaExistTypeToJavaWildcardGeneric1 {
//採用Scala中的存在型別與Java中的能匹符泛型進行互操作
def printList(list: List[T] forSome {type T}):Unit={
//因為我們引入了import scala.collection.JavaConversions._
//所以可以直接呼叫foreach方法
list.foreach(println)
}
//上面的函式與下面的等同
def printList2(list: List[_]):Unit={
list.foreach(println)
}
}
object Main extends App{
val s=new ScalaExistTypeToJavaWildcardGeneric1
s.printList(JavaWildcardGeneric.getList)
s.printList2(JavaWildcardGeneric.getList)
}
4. Scala與Java間的異常處理互操作
Java中的異常處理具有如下形式:
package cn.scala.xtwy.ScalaAndJavaException;
import java.io.File;
import java.io.IOException;
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
public class JavaExceiptionDemo {
public static void main(String[] args) {
File file = new File("a.txt");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Scala中的異常處理是通過模式匹配來實現的,程式碼如下:
package cn.scala.xtwy.ScalaAndJavaException
import java.io.File
/**
* Created by 搖擺少年夢 on 2015/8/16.
*/
object ScalaExceptionDemo extends App{
val file: File = new File("a.txt")
if (!file.exists) {
try {
file.createNewFile
}
catch {
//通過模式匹配來實現異常處理
case e: IOException => {
e.printStackTrace
}
}
}
}
上面給的例子是Scala如何捕獲Java中丟擲的異常,下面的例子給出的是Java如何捕獲Scala中宣告的異常,程式碼如下:
package cn.scala.xtwy.ScalaAndJavaException
class ScalaThrower {
//Scala利用註解@throws宣告丟擲異常
@throws(classOf[Exception])
def exceptionThrower {
throw new Exception("Exception!")
}
}
//Java中呼叫ScalaThrower(Scala類),然後捕獲其丟擲的異常
public class JavaCatchScalaThrower {
public static void main(String[] args){
ScalaThrower st=new ScalaThrower();
try{
st.exceptionThrower();
}catch (Exception e){
e.printStackTrace();
}
}
}
通過本節,我們基本能掌握Scala與Java的互操作,當然這裡面還有很多內容沒有涉及,但在日常開發工作當中,掌握本節講的內容便可以應付絕大多數互操作問題。
新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊