Scala入门到精通——第二十八节 Scala与JAVA互操作

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 本节主要内容JAVA中调用Scala类Scala中调用JAVA类Scala类型参数与JAVA泛型互操作Scala与Java间的异常处理互操作1. JAVA中调用Scala类Java可以直接操作纵Scala类,如同Scala直接使用Java中的类一样,例如://在Person.scala文件中定义Scala语法的Person类package cn.

本节主要内容

  1. JAVA中调用Scala类
  2. Scala中调用JAVA类
  3. Scala类型参数与JAVA泛型互操作
  4. 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相关技术资讯
这里写图片描述

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
8天前
|
算法 Java 开发者
Java 编程入门:从零到一的旅程
本文将带领读者开启Java编程之旅,从最基础的语法入手,逐步深入到面向对象的核心概念。通过实例代码演示,我们将一起探索如何定义类和对象、实现继承与多态,并解决常见的编程挑战。无论你是编程新手还是希望巩固基础的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
10天前
|
存储 Java 程序员
Java中的集合框架:从入门到精通
【8月更文挑战第30天】在Java的世界里,集合框架是一块基石,它不仅承载着数据的存储和操作,还体现了面向对象编程的精髓。本篇文章将带你遨游Java集合框架的海洋,从基础概念到高级应用,一步步揭示它的奥秘。你将学会如何选择合适的集合类型,掌握集合的遍历技巧,以及理解集合框架背后的设计哲学。让我们一起探索这个强大工具,解锁数据结构的新视角。
|
12天前
|
Java 程序员 UED
Java中的异常处理:从入门到精通
【8月更文挑战第28天】在Java编程的世界里,异常处理是一块基石,它确保了程序的健壮性和可靠性。本文将通过深入浅出的方式,带你了解Java异常处理的基本概念、分类、以及如何有效地捕获和处理异常。我们将一起探索try-catch-finally结构的奥秘,并学习如何使用throws关键字声明方法可能会抛出的异常。此外,我们还会讨论自定义异常类的创建和使用,以及最佳实践。无论你是Java新手还是有一定经验的开发者,这篇文章都将为你提供宝贵的知识,帮助你编写出更加稳定和可靠的代码。
|
12天前
|
编解码 网络协议 Oracle
java网络编程入门以及项目实战
这篇文章是Java网络编程的入门教程,涵盖了网络编程的基础知识、IP地址、端口、通讯协议(TCP和UDP)的概念与区别,并提供了基于TCP和UDP的网络编程实例,包括远程聊天和文件传输程序的代码实现。
java网络编程入门以及项目实战
|
21天前
|
前端开发 IDE Java
"揭秘前端转Java的秘径:SpringBoot Web极速入门,掌握分层解耦艺术,让你的后端代码飞起来,你敢来挑战吗?"
【8月更文挑战第19天】面向前端开发者介绍Spring Boot后端开发,通过简化Spring应用搭建,快速实现Web应用。本文以创建“Hello World”应用为例,展示项目基本结构与运行方式。进而深入探讨三层架构(Controller、Service、DAO)下的分层解耦概念,通过员工信息管理示例,演示各层如何协作及依赖注入的使用,以此提升代码灵活性与可维护性。
30 2
|
8天前
|
Java 程序员 UED
Java 中的异常处理:从入门到精通
【8月更文挑战第31天】在Java编程的世界中,异常处理是保持应用稳定性的重要机制。本文将引导你理解异常的本质,学会如何使用try-catch语句来捕获和处理异常,并探索自定义异常类的魅力。我们将一起深入异常的世界,让你的代码更加健壮和用户友好。
|
8天前
|
Java 数据库连接 开发者
Java中的异常处理:从入门到精通
【8月更文挑战第31天】 在编程世界中,错误和异常就像是不请自来的客人,总是在不经意间打扰我们的程序运行。Java语言通过其异常处理机制,为开发者提供了一套优雅的“待客之道”。本文将带你走进Java异常处理的世界,从基础语法到高级技巧,再到最佳实践,让你的程序在面对意外时,也能从容不迫,优雅应对。
|
8天前
|
Java 开发者
Java 中的异常处理:从入门到精通
【8月更文挑战第31天】在Java的世界中,异常处理是保持程序健壮性的基石。本文将带你探索Java异常处理的奥秘,从基本的try-catch语句到深入理解自定义异常和最佳实践。你将学会如何优雅地处理错误,确保你的代码不仅能够面对意外情况,还能从中恢复。让我们一起开启这段旅程,掌握让程序更加稳定和可靠的技巧吧!
|
11天前
|
机器学习/深度学习 Java TensorFlow
深度学习中的图像识别:从理论到实践Java中的多线程编程入门指南
【8月更文挑战第29天】本文将深入探讨深度学习在图像识别领域的应用,从基础理论到实际应用案例,带领读者一步步理解如何利用深度学习技术进行图像识别。我们将通过一个简单的代码示例,展示如何使用Python和TensorFlow库实现一个基本的图像识别模型。无论你是初学者还是有一定经验的开发者,都能从中获得启发和学习。 【8月更文挑战第29天】在Java世界里,线程是程序执行的最小单元,而多线程则是提高程序效率和响应性的关键武器。本文将深入浅出地引导你理解Java多线程的核心概念、创建方法以及同步机制,帮助你解锁并发编程的大门。
|
15天前
|
存储 Java 开发者
Java数据类型:从入门到精通,你不得不看的“宝典”
Java数据类型:从入门到精通,你不得不看的“宝典”
32 0