Lambda表达式的序列化 与 强制类型转换——(Comparator> & Serializable)

这篇文章先引出问题,再给出一个例子重现问题。最后通过一些较为官方的资料,来说明引起问题的原因,原理。

文章目录

    • 1. 发现问题
    • 2. 重现问题
    • 3. 分析问题
    • 参考文献

1. 发现问题

在文章开始之前我们先看看一段JDK 8中java.util.Map的代码。

public interface Map<K,V> {
	// ... 省略与本文无关的代码
	interface Entry<K,V> {
		// ...
		public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
	           return (Comparator<Map.Entry<K, V>> & Serializable)
	               (c1, c2) -> c1.getKey().compareTo(c2.getKey());
	       }
	       // ...
	}
	// ... 

我们着重关注一下这段代码:

public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
  return (Comparator<Map.Entry<K, V>> & Serializable)
       (c1, c2) -> c1.getKey().compareTo(c2.getKey());
}

我们知道这句是lambda表达式:

(c1, c2) -> c1.getKey().compareTo(c2.getKey())

那么这句呢?

(Comparator<Map.Entry<K, V>> & Serializable)

我们知道&按位与操作,但是两个数据类型按位与?没见过?
实际上,这是强制类型转换,转换成可序列化的Comparator>对象&的意思是且的意思。

这种强制类型转换可以将一个对象直接转换为Serializable对象。

2. 重现问题

我们做个实验来验证一下吧:

public class CollectionTest {

    public static String datafile = "C:\\Users\\xxx\\Desktop\\test\\out.out";

    interface Function{
        void func(String s);
    }

    public static Function get(){
        return (Function & Serializable) ( (s)->System.out.println(s) );
    }

    public static void main(String[] args) throws IOException {
        Function function = get();
        ObjectOutputStream out = new ObjectOutputStream(
                                  new BufferedOutputStream(
                                   new FileOutputStream(datafile)));
        out.writeObject(function);
        System.out.println("输出完成...");
        out.close();
    }
}

3. 分析问题

首先,这是Java语法的支持,是语法的问题,我们现在关注一下语法。
Java语言对强制类型转换表达式(CastExpression)是按照如下方式定义的:

CastExpression:
	( PrimitiveType ) UnaryExpression 
	( ReferenceType {AdditionalBound} ) UnaryExpressionNotPlusMinus 
	( ReferenceType {AdditionalBound} ) LambdaExpression

其中AdditionalBound的定义如下:

AdditionalBound:
	& InterfaceType

综上,我们知道强制类型转换表达式(CastExpression)支持lambda表达式,Lambda表达式需要符合如下的语法
( ReferenceType & InterfaceType ) LambdaExpression
也就是说Lambda表达式被转化为引用类型和接口类型两种类型

其实,引用类型后面可以跟一个或者多个接口类型,但是所有的类型都需要满足如下条件:

  • ReferenceType必须要是引用类型或者接口类型
  • 所有的数据类型进行类型消除后(类型消除是什么?点击查看博文),必须两两不同。
  • 没有两个列出的类型可以是同一通用接口的不同参数化的子类型。

下面我们再给出两个例子结尾:

Runnable r = (Runnable & Serializable)() -> System.out.println("Serializable!");
public class Test {
    static Object bar(String s) {
        return "make serializable";
    }

    void m () {
        SAM s1 = (SAM & Serializable) Test::bar;
        SAM s2 = (SAM & Serializable) t -> "make serializable";
    }

    interface SAM {
        Object action(String s);
    }
}

参考文献

  1. how-to-serialize-a-lambda
  2. jls-15.16
  3. jls-4.6

你可能感兴趣的:(Java)