Scala 中方法和函数底层Java 实现
Scala 源代码
// FuncMeth.scala
package com.abc.scala
object FuncMeth {
// 命名函数,又叫方法 Method,完整写法如下
def meth1(name1: String, name2: String): String = {
return "Method1 " + name1 + "_" + name2
}
// 最精简的无参命名函数可以写成
def meth2 = "Method2 Nobody"
// 匿名函数,又叫函数 Function,这里赋值给了func1
// 注意:匿名函数不用写返回值类型,Scala 会根据 => 右边最后一行代码的结果进行类型推断
val func1 = (name1: String, name2: String) => {
// 匿名函数中不可以用return 关键字,否则底层Java 会抛异常导致整个程序结束
"Function1 " + name1 + "_" + name2 // 最后一行代码的结果将作为返回值
}
// 匿名函数的另一种写法(语法糖),类似于 val name: String = "ABC"
// val 函数名: (函数类型) = (形参) => { 函数体定义 },完整写法如下
val func2: ((String, String) => String) = (name1, name2) => {
"Function2 " + name1 + "_" + name2
}
// 等号 = 左边是变量名和类型,这里是"函数类型",右边是值,这里是函数具体定义
// 第一个 => 表示函数类型,左边是输入参数类型,右边是返回值类型
// 第二个 => 才是函数的完整定义,左边是函数形参列表,右边则是函数的具体实现
// 另外,使用占位符 _ 可将以上函数定义简化,以及括号在没有歧义的情况下也可省略
val func3: (String, String) => String = { "Function3 " + _ + "_" + _ }
// 例如,函数类型(=> Unit)可以表示没有输入也没有输出的一段代码块{}
// Scala 中函数Function 作为一种数据类型可以赋值给变量
// 跟其他变量一样,它也可以作为参数传入高阶函数中
// 高阶函数:输入或输出(即参数或返回值)类型为函数的函数
// 但是方法Method 需要转换成函数后才可以作为参数,如下
// Method 转换成Function
val meth_1 = meth1 _
// 如果去掉下划线则表示将meth1 的返回值赋给变量meth_1
// 如将方法作为参数,Scala 会将其自动转换为函数,效果与本例相同
// 程序运行main 入口
def main(args: Array[String]): Unit = {
println(meth1("ABC", "BBB")) // Method1 ABC_BBB
println(meth2) // Method2 Nobody
println(func1("ABC", "BBB")) // Function1 ABC_BBB
println(func2("ABC", "BBB")) // Function2 ABC_BBB
println(func3("ABC", "BBB")) // Function3 ABC_BBB
println("func1=" + func1) // func1=<function2>
println("meth_1=" + meth_1) // meth_1=<function2>
println(meth_1("ABC1", "BBB1")) // Method ABC1_BBB1
}
}
Scala 编译运行
scalac FuncMeth.scala && scala com.abc.scala.FuncMeth
Java 模拟代码
package com.abc.from.scala;
public class FuncMeth {
public static void main(String[] paramArrayOfString) {
FuncMeth$.MODULE$.main(paramArrayOfString);
}
public static Function2<String, String, String> func1() {
return FuncMeth$.MODULE$.func1();
}
public static String meth1(String paramString1, String paramString2) {
return FuncMeth$.MODULE$.meth1(paramString1, paramString2);
}
}
final class FuncMeth$ {
public static final FuncMeth$ MODULE$;
private final Function2<String, String, String> func1;
private final Function2<String, String, String> func2;
private final Function2<String, String, String> func3;
private final Function2<String, String, String> meth_1;
static {
MODULE$ = new FuncMeth$();
}
public String meth1(String name1, String name2) {
return (new StringBuilder()).append("Method1 ").append(name1).append("_").append(name2).toString();
}
public String meth2() { return "Method2 Nobody"; }
private FuncMeth$() {
this.func1 = new FuncMeth$$anonfun$1();
this.func2 = new FuncMeth$$anonfun$2();
this.func3 = new FuncMeth$$anonfun$3();
this.meth_1 = new FuncMeth$$anonfun$4();
}
public Function2<String, String, String> func1() {
return this.func1;
}
public final class FuncMeth$$anonfun$1 extends AbstractFunction2<String, String, String> {
public final String apply(String name1, String name2) {
return (new StringBuilder()).append("Function1 ").append(name1).append("_").append(name2).toString();
}
}
public Function2<String, String, String> func2() {
return this.func2;
}
public final class FuncMeth$$anonfun$2 extends AbstractFunction2<String, String, String> {
public final String apply(String name1, String name2) {
return (new StringBuilder()).append("Function2 ").append(name1).append("_").append(name2).toString();
}
}
public Function2<String, String, String> func3() {
return this.func3;
}
public final class FuncMeth$$anonfun$3 extends AbstractFunction2<String, String, String> {
public final String apply(String x$1, String x$2) {
return (new StringBuilder()).append("Function3 ").append(x$1).append("_").append(x$2).toString();
}
}
public Function2<String, String, String> meth_1() {
return this.meth_1;
}
public final class FuncMeth$$anonfun$4 extends AbstractFunction2<String, String, String> {
public final String apply(String name1, String name2) {
return FuncMeth$.MODULE$.meth1(name1, name2);
}
}
public void main(String[] args) {
System.out.println(meth1("ABC", "BBB"));
System.out.println(meth2());
System.out.println(func1().apply("ABC", "BBB"));
System.out.println(func2().apply("ABC", "BBB"));
System.out.println(func3().apply("ABC", "BBB"));
System.out.println((new StringBuilder()).append("func1=").append(func1()).toString());
System.out.println((new StringBuilder()).append("meth_1=").append(meth_1()).toString());
System.out.println(meth_1().apply("ABC1", "BBB1"));
}
}
interface Function2<T1, T2, T3> {
T3 apply(T1 arg1, T2 arg2);
}
abstract class AbstractFunction2<T1, T2, T3> implements Function2<T1, T2, T3> {
abstract public T3 apply(T1 arg1, T2 arg2);
@Override
public String toString() {
return "<function2> JD";
}
}
Java 编译运行
javac -d . FuncMeth.java && java com.abc.from.scala.FuncMeth
结论
- Scala def 的命名函数和Java 中定义方法一样,函数定义写在方法体里,为了跟匿名函数作区分称其为"方法"
- Scala 的匿名函数在底层Java 中是个FuncMeth$
a
n
o
n
f
u
n
anonfun
anonfunN 对象,函数定义写在该对象的apply 方法里
- Scala 中方法转函数的本质是通过一个FuncMeth$
a
n
o
n
f
u
n
anonfun
anonfunN 对象的apply 方法调用被转换的方法
- Scala 方法或函数的语法糖简化写法详见以上章节的源码及注释
参考文档
Scala 符号含义(官方说明)
Scala 传名参数
Scala => 含义
Scala 函数和方法的区别