代码之家  ›  专栏  ›  技术社区  ›  user826955

scala中的对象实例化变量

  •  1
  • user826955  · 技术社区  · 6 年前

    我对斯卡拉真的很陌生,而且我现在正在完成我的旅行。( https://docs.scala-lang.org/tour/variances.html )。

    现在,看看图书馆( akka-http ,我偶然发现了这样的代码:

    def fetchItem(itemId: Long): Future[Option[Item]] = Future {
        orders.find(o => o.id == itemId)
      }
    

    我不太明白语法,或者更准确地说, = Future { 部分。正如我所了解的,方法的语法是 def [methodName]([Arguments])(:[returnType]) = [codeblock]

    然而,上面的区别在于 Future 在“代码块”前面。这是某种对象实例化吗?因为找不到有关此语法的文档,所以我尝试了以下播放代码:

    {
      val myCat:Cat      = new Cat("first cat")
      val myOtherCat:Cat = Cat { "second cat" }
      val myThirdCat:Cat = MyObject.getSomeCat
    }
    
    ...
    
    object MyObject
    {
       def getSomeCat: Cat = Cat 
       {
         "blabla"
       }
    }
    

    所有这些都是有效的,因为它创造了一个新的 Cat 对象。所以看起来 new Cat(args) 等于 Cat { args }

    但不应该 def getSomeCat: Cat = Cat 用代码块定义一个方法,而不是实例化一个新的 对象?我很困惑。

    3 回复  |  直到 6 年前
        1
  •  3
  •   slouc    6 年前

    我认为这里有两件事:

    1。 这个 [codeblock] 在方法语法中,不必用括起来。如果只有一个表达式,就可以省略它们。

    例如。

    def add(x: Int, y: Int) = x + y
    

    def add(x: Int, y: Int) = Future { x + y }
    

    2。 每个类都可以有它的伴生对象定义一个 apply() 方法,可以在不显式地说“apply”的情况下调用(这是特殊的scala语法结构)。这允许我们通过浏览伴生对象来构造类的实例,并且由于可以省略“apply”,乍一看,它看起来像是浏览类本身,而不需要“new”关键字。

    没有对象:

    class Cat(s: String)
    
    val myFirstCat: Cat = new Cat("first cat") // OK
    val mySecondCat: Cat = Cat("second cat") // error
    

    现在有了这个物体:

    class Cat(s: String)
    
    object Cat {
      def apply(s: String) = new Cat(s)
    }
    
    val myFirstCat: Cat = new Cat("first cat") // OK
    val mySecondCat: Cat = Cat.apply("second cat") // OK 
    val myThirdCat: Cat = Cat("third cat") // OK (uses apply under the hood)
    val myFourthCat: Cat = Cat { "fourth cat" } // OK as well
    

    注意第四个cat调用如何与大括号一起工作很好,因为方法可以传递代码块(将传递块中最后计算的值,就像在函数中一样)。

    三。 case类是另一个稍微“特殊”的scala构造,从某种意义上说,它们通过自动为您“幕后”提供一些东西(包括与apply()关联的伴生对象)来为您提供便利。

    case class Cat(s: String)
    
    val myFirstCat: Cat = new Cat("first cat") // OK
    val mySecondCat: Cat = Cat.apply("second cat") // OK
    val myThirdCat: Cat = Cat("third cat") // OK
    

    在你的案例中,未来发生的是第二个,与“第四只猫”相同。关于你的问题关于 new Cat(args) 相当于 Cat { args } 最有可能是第三种情况- Cat 是一个案例类。或者它的伴生对象显式定义 应用程序() 方法。

        2
  •  1
  •   Tim    6 年前

    简短的回答是“是”,即 Future 代码是一个对象声明。

    你的 Cat 类有一个 String 参数,可以使用 Cat(<string>) . 如果要计算字符串的值,可以使用 {} 正如您在示例中所做的。此块可以包含任意代码,块的值将是块中最后一个表达式的值,该表达式必须是类型

    这个 Future[T] 类具有类型为的单个参数 T 并且可以使用 Future(T) . 您可以像以前一样传递任意代码块,只要它返回类型的值 T

    所以创造一个 未来 就像创建其他对象一样。这个 fetchItem 代码只是创建一个 未来 对象并返回它。

    然而 有一种微妙的 未来 其中,参数定义为“按名称调用”参数,而不是默认的“按值调用”参数。这意味着在使用前不会对其进行评估,并且每次使用时都会对其进行评估。如果是 未来 稍后会对参数进行一次评估,可能会在不同的线程上进行评估。如果使用一个块来计算参数,那么每次使用参数时都会执行整个块。

    scala有非常强大的语法和一些有用的快捷方式,但是要适应它还需要一段时间!

        3
  •  1
  •   Ra Ka    6 年前

    典型的方法结构如下:

    case class Bar(name: String)
    def foo(param: String): Bar = {
      // code block.
    }    
    

    但是,scala在方法定义方面非常灵活。灵活性之一是,如果方法块包含单个表达式,则可以忽略大括号 { }

    def foo(param: String): Bar = {
      Bar("This is Bar")  //Block only contains single expression.
    } 
    // Can be written as:
    def foo(param: String): Bar = Bar("This is Bar")
    
    // In case of multiple expressions:
    def foo(param: String): Bar = {
      println("Returning Bar...")
      Bar("This is Bar")
    } 
    def foo(param: String): Bar = println("Returning Bar...") Bar("This is Bar") //Fails
    def foo(param: String): Bar = println("Returning Bar..."); Bar("This is Bar") //Fails
    def foo(param: String): Bar = {println("Returning Bar..."); Bar("This is Bar")} // Works
    

    同样,在您的代码中, fetchItem 仅包含单个表达式- Future {orders.find(o => o.id == itemId)} 返回新的未来(未来的实例) Option[Item] ,因此支撑 {} 是可选的。但是,如果需要,可以在大括号内编写它,如下所示:

    def fetchItem(itemId: Long): Future[Option[Item]] = {
        Future {
            orders.find(o => o.id == itemId)
        }
    }
    

    类似地,如果一个方法只使用一个参数,则可以使用大括号。也就是说,你可以调用 fetchItems AS:

    fetchItem(10)
    fetchItem{10}
    fetchItem{
        10
    }
    

    那么,为什么要用大括号呢? {} 而不是括号 ( ) ?

    因为,可以在大括号内提供多个代码块,当需要执行多个计算并返回该计算结果的值时,就需要这种情况。例如:

    fetchItem{
        val x = 2
        val y = 3
        val z = 2
        (x + y)*z  //final result is 10 which is passed as parameter.
    }
    
    // In addition, using curly braces will make your code more cleaner e.g. in case of higher ordered functions. 
    def test(id: String => Unit) = ???
    test {
        id => {
            val result: List[String] = getDataById(x)
            val updated = result.map(_.toUpperCase)
            updated.mkString(",")
        }
    }
    

    现在,来谈谈你的案子,当你 Future{...} ,您正在调用 apply(...) 采用函数文本的scala未来伴生对象的方法 body: =>T 作为参数并返回新的未来。

    //The below code can also be written as:
    Future {
        orders.find(o => o.id == itemId)
    }
    //Can be written as:
    Future(orders.find(o => o.id == itemId))
    Future.apply(orders.find(o => o.id == itemId))
    
    // However, braces are not allowed with multiple parameter.
    def foo(a:String, b:String) = ???
    foo("1","2") //work
    foo{"1", "2"} //won't work.