What is the difference between different function

2019-08-19 04:31发布

问题:

This question is heavily related to my other question (and may lead to me solving that one) but is definetely different.

how to allow passing in a => AnyRef function and call that function

I have been playing with different function creations and I am frankly having trouble creating an anonymous function of type => AnyRef and => String. I can create a function of type () => AnyRef and () => String I think.

Example 1 I have the following code

def debugLazyTest2(msg: => String) : Unit = {
  System.out.println(msg)
}

//and client code
  val function: () => String = () => {
    executed = true
    "asdf"+executed+" hi there"
  }
  log2.debugLazyTest2(function)

but the compile error says found: () => String which makes sense but then says "required: String" instead of "required: => String"

What is going on here?

Example 2 to get even more bizarre, I have this code which compiles while above did not compile

def debugLazyTest(msg: => AnyRef) : Unit = {
  System.out.println(msg.toString)
}

//and client code which compiles!!!!
  val function: () => AnyRef = () => {
    executed = true
    "asdf"+executed+" hi there"
  }
  log2.debugLazyTest(function)

This code compiles though it doesn't work the way I would like as the library can't seem to invoke the function before toString is called (that is in my other thread and is a separate question).

Any ideas as to what is going on here?

thanks, Dean

回答1:

If you wrote this it would work:

log2.debugLazyTest2(function())

msg is a by-name parameter, not a function. You have to pass in an expression of type String (or AnyRef in the second example)

The second example compiles because the () => AnyRef you pass in is actually also an AnyRef, since a function is an AnyRef. But then it is the function itself that is printed, not the result of executing it.



回答2:

Consider the following code:

scala> def test(x: String) = debugLazyTest2(x)
test: (x: String)Unit

If we then run (in Scala 2.11.2):

:javap test

And trim a bit of the generated output, we see the following:

  public void test(java.lang.String);
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=2, args_size=2
         0: getstatic     #19                 // Field .MODULE$:L;
         3: new           #21                 // class $anonfun$test$1
         6: dup
         7: aload_1
         8: invokespecial #23                 // Method $anonfun$test$1."<init>":(Ljava/lang/String;)V
        11: invokevirtual #27                 // Method .debugLazyTest2:(Lscala/Function0;)V
        14: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      15     0  this   L;
               0      15     1     x   Ljava/lang/String;
      LineNumberTable:
        line 8: 0

And then we consider the signature for debugLazyTest2:

 public void debugLazyTest2(scala.Function0<java.lang.String>);

The key line is:

         3: new           #21                 // class $anonfun$test$1

If I am reading the code correctly, we are instantiating a new instance of the class $anonfun$test$1 - and passing that new anonymous Function0[String] to debugLazyTest2. This makes our test method equivalent to the following:

def test(x: String) = debugLazyTest2(new Function0[String] {
    def apply(): String = x
})

When we consider passing in an instance of Function0[String] to debugLazyTest2 making the same transformation, we get:

debugLazyTest2(function)

which becomes:

debugLazyTest2(new Function0[String] {
    def apply(): Function0[String] = function
})

which, of course, does not compile, because apply(): Function0[String] does not match the required type apply(): String (hence the error message).

Actually invoking the function instead of returning it works:

debugLazyTest2(function())

becomes:

debugLazyTest2(new Function0[String] {
    def apply(): String = function()
})