02 Scala 中方法和函数底层Java 实现

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 编译运行

# 先将以上代码写入文件FuncMeth.scala 然后执行
scalac FuncMeth.scala && scala com.abc.scala.FuncMeth

Java 模拟代码

// FuncMeth.java
package com.abc.from.scala;

public class FuncMeth {
	// Scala 中object FuncMeth 里定义和调用的函数实际上是Java 中FuncMeth 类的静态方法
	// 这些FuncMeth 类的静态方法内部则调用了FuncMeth$ 的单例对象MODULE$ 的同名方法
	public static void main(String[] paramArrayOfString) {
		FuncMeth$.MODULE$.main(paramArrayOfString);
	}

	// 其他与main 平级的静态方法定义如下,但程序中并没用到,可能是反编译不准确
	public static Function2<String, String, String> func1() {
		return FuncMeth$.MODULE$.func1();
	}

	// 真正调用的入口在FuncMeth$.MODULE$.main,所以反编译的代码仅供参考,不可完全照搬
	public static String meth1(String paramString1, String paramString2) {
		return FuncMeth$.MODULE$.meth1(paramString1, paramString2);
	}
}

// FuncMeth$ 类不能被继承,构造函数私有,对外只提供单例对象MODULE$
final class FuncMeth$ {
	// Scala 中object FuncMeth 对应底层Java 的FuncMeth$ 静态对象MODULE$
	public static final FuncMeth$ MODULE$;
	// Scala 中object FuncMeth 里定义的变量
	// 实际上是底层Java 中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;

	// FuncMeth$ 被加载时创建本类对象MODULE$,单例模式的恶汉式
	static {
		MODULE$ = new FuncMeth$();
	}


	/*
	注意!!!重点一
	Scala 的命名函数和Java 中定义方法基本一样,函数定义写在方法体里,为了跟匿名函数作区分称其为"方法"
	def meth1(name1: String, name2: String): String = {
    return "Method " + name1 + "_" + name2
  }
	*/
	public String meth1(String name1, String name2) {
		return (new StringBuilder()).append("Method1 ").append(name1).append("_").append(name2).toString();
	}

	// def meth2 = "Method2 Nobody"
	public String meth2() { return "Method2 Nobody"; }


	/*
	注意!!!重点二
	Scala 的匿名函数在底层Java 中是个FuncMeth$$anonfun$N 对象,函数定义写在该对象的apply 方法里
	接收匿名函的变量对应FuncMeth$ 的一个属性,FuncMeth$$anonfun$N 类最后的N 代表是第N 个匿名函数
	*/
	// 构造函数私有,单例模式的必要条件
	private FuncMeth$() {
		// Scala 匿名函数与Java 对象属性对应关系如下
		// val func1 = (name1: String, name2: String) => {
		//   "Function " + name1 + "_" + name2
		// }
		this.func1 = new FuncMeth$$anonfun$1();  // 第1 个匿名函数,赋值给属性func1

		// val func2: ((String, String) => String) = (name1, name2) => {
		//   "Function2 " + name1 + "_" + name2
		// }
		this.func2 = new FuncMeth$$anonfun$2();  // 第2 个匿名函数,赋值给属性func2

		// val func3: (String, String) => String = { "Function3 " + _ + "_" + _ }
		this.func3 = new FuncMeth$$anonfun$3();  // 第3 个匿名函数,赋值给属性func3

		// val meth_1 = meth1 _
		this.meth_1 = new FuncMeth$$anonfun$4();  // 第4 个匿名函数,赋值给属性meth_1
	}

	public Function2<String, String, String> func1() {
		return this.func1;
	}

	// 第1 个匿名函数实现类
	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;
	}

	// 第2 个匿名函数实现类
	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;
	}

	// 第3 个匿名函数实现类
	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();
		}
	}


	/*
	注意!!!重点三
	Scala 中 Method _ 的下划线表示将方法Method 转换为函数Function,其本质是
	新建一个FuncMeth$$anonfun$N 对象,然后在该对象的apply 方法中调用被转换的方法
	*/
	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);  // 被转换的方法meth1 在这里被调用了
		}
	}

	// MODULE$.main 才是真正的程序入口
	public void main(String[] args) {
		System.out.println(meth1("ABC", "BBB"));  // 方法直接调用
		System.out.println(meth2());  // 同上
		System.out.println(func1().apply("ABC", "BBB"));  // 函数实际上调用的是apply 方法
		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"));  // 转换后的函数也是调用apply 方法
	}
}

/*
Function2,2 表示该匿名函数有2 个输入参数
T1 T2 是输入参数的类型
T3    是返回值的类型
*/
interface Function2<T1, T2, T3> {
	T3 apply(T1 arg1, T2 arg2);
}

/*
泛型抽象类AbstractFunction2 实现泛型接口Function2
Scala 中匿名函数在底层的Java 类都继承自AbstractFunctionN (N 表示输入参数的个数)
*/
abstract class AbstractFunction2<T1, T2, T3> implements Function2<T1, T2, T3> {
	abstract public T3 apply(T1 arg1, T2 arg2);

	@Override  // 重写该类的toString 方法
	public String toString() {
		return "<function2> JD";
	}
}

Java 编译运行

# 先将以上代码写入文件FuncMeth.scala 然后执行
javac -d . FuncMeth.java && java com.abc.from.scala.FuncMeth

结论

  1. Scala def 的命名函数和Java 中定义方法一样,函数定义写在方法体里,为了跟匿名函数作区分称其为"方法"
  2. Scala 的匿名函数在底层Java 中是个FuncMeth$ a n o n f u n anonfun anonfunN 对象,函数定义写在该对象的apply 方法里
  3. Scala 中方法转函数的本质是通过一个FuncMeth$ a n o n f u n anonfun anonfunN 对象的apply 方法调用被转换的方法
  4. Scala 方法或函数的语法糖简化写法详见以上章节的源码及注释

参考文档

Scala 符号含义(官方说明)
Scala 传名参数
Scala => 含义
Scala 函数和方法的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值