Scala中def foo = {}和def foo()= {}之间有什么区别?

问题描述 投票:50回答:4

给定以下用于在Scala中定义函数的构造,您能解释一下它们之间的区别,以及它们的含义是什么?

def foo = {}

def foo() = {}

更新

感谢您的快速回复。这些都很棒。对我来说唯一的问题是:

如果我省略括号,是否还有办法传递函数?这是我在repl中得到的:

scala> def foo = {}
foo: Unit

scala> def baz() = {}
baz: ()Unit

scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit

scala> test(foo)
<console>:10: error: type mismatch;
 found   : Unit
 required: () => Unit
              test(foo)
                   ^

scala> test(baz)
res1: () => Unit = <function0>

更新2012-09-14

以下是我注意到的一些类似问题:

  1. Difference between function with parentheses and without
  2. Scala methods with no arguments
scala programming-languages functional-programming jvm jvm-languages
4个回答
36
投票

如果在定义中包含括号,则可以在调用方法时将其省略。如果在定义中省略它们,则在调用方法时无法使用它们。

scala> def foo() {}
foo: ()Unit

scala> def bar {}
bar: Unit

scala> foo

scala> bar()
<console>:12: error: Unit does not take parameters
       bar()
          ^

此外,您可以使用更高阶的功能执行类似操作:

scala> def baz(f: () => Unit) {}
baz: (f: () => Unit)Unit

scala> def bat(f: => Unit) {}
bat: (f: => Unit)Unit

scala> baz(foo)    

scala> baz(bar)
<console>:13: error: type mismatch;
 found   : Unit
 required: () => Unit
       baz(bar)
           ^
scala> bat(foo)

scala> bat(bar)  // both ok

在这里baz将只采取foo()而不是bar。这是什么用,我不知道。但它确实表明类型是不同的。


35
投票

让我复制我在a duplicated question上发布的答案:

可以使用或不使用括号()来定义0-arity的Scala方法。这用于通知用户该方法具有某种副作用(如打印出std out或销毁数据),而不是没有的那种,后来可以实现为val

Programming in Scala

这种无参数的方法在Scala中很常见。相比之下,使用空括号定义的方法(例如def height():Int)称为empty-paren方法。建议的约定是在没有参数时使用无参数方法,并且该方法仅通过读取包含对象的字段来访问可变状态(特别是,它不会改变可变状态)。

该公约支持统一访问原则[...]

总而言之,鼓励Scala中的样式定义不带参数且没有副作用的方法作为无参数方法,即省略空括号。另一方面,您永远不应该定义一个没有括号的副作用的方法,因为那个方法的调用看起来像一个字段选择。


4
投票

要回答你的第二个问题,只需添加一个_

scala> def foo = println("foo!")
foo: Unit

scala> def test(arg: () => Unit) = { arg }
test: (arg: () => Unit)() => Unit

scala> test(foo _)
res10: () => Unit = <function0>

scala> test(foo _)()
foo!

scala>            

0
投票

我建议总是用以下函数开始定义:

def bar {}

并且只有在您被迫时才将其更改为:

def bar() {}

原因:让我们从可能的用途角度考虑这两个功能。他们如何被吸入以及他们可以通过哪里。

我根本不称这是一个功能:

def bar {}

它可以被调用为:

bar

但不是功能:

bar()

当我们使用call-by-name参数定义高阶函数时,我们可以使用此栏:

def bat(f: => Unit) {
    f //you must not use (), it will fail f()
}

我们应该记住,=> Unit - 甚至不是一个功能。你完全无法使用thunk,就好像它是一个函数,因为你不能选择将它视为要存储或传递的函数值。您只能触发实际参数表达式的评估(任意数量)。 Scala: passing function as block of code between curly braces

使用()定义的函数具有更大的使用范围。它可以在与bar相同的上下文中使用:

def foo() = {}
//invokation:
foo
//or as a function:
foo()

它可以通过call-by-name参数传递给函数:

bat(foo)

另外,如果我们定义一个更高阶的函数,它不接受按名称调用的pamameter,而是一个真正的函数:

def baz(f: () => Unit) {}

我们也可以将foo传递给baz

baz(foo)

我们可以看到像foo这样的标准函数有更大的使用范围。但是使用没有()定义的函数加上定义高阶函数,接受call-by-name参数,让我们使用更清晰的语法。

如果您不尝试存档更好,更易读的代码,或者您需要能够将您的代码片段传递给使用call-by-name参数定义的函数和使用实际函数定义的函数,那么请定义作为标准功能:

def foo() {}

如果您希望编写更清晰易读的代码,并且您的函数没有副作用,请将函数定义为:

def bar {}

PLUS尝试定义您的高阶函数以接受按名称调用参数,但不接受函数。只有在您被迫时,才会使用上一个选项。

© www.soinside.com 2019 - 2024. All rights reserved.