【问题标题】:Scala, why do I not need to import deduced typesScala,为什么我不需要导入推导类型
【发布时间】:2016-12-13 21:27:38
【问题描述】:

我觉得我应该以我正在使用 sbt 构建我的项目这一事实作为开头。

我的问题是,如果在编译时一个方法返回一个未导入类型的东西,在我调用该方法的文件中,只要我使用类型推断,一切都会编译。一旦我尝试将未导入的类型分配给我使用函数的返回值创建的 var/val,就会出现编译器错误。

假设我在两个包中有两个类。包main 中的类App 和包libraries 中的类Imported。让我们进一步说,我们在 main 包中有一个类 ImportedFactory,并且这个类有一个用于创建 Imported 类型的对象的方法。

这段代码编译得很好:

class App() {
    // method return object of type Imported
    val imp = ImportedFactory.createImportedObject() 
}

这不是:

class App() {
    // method return object of type Imported
    val imp : Imported = ImportedFactory.createImportedObject() 
}

这又是这样:

import libraries.Imported

class App() {
    // method return object of type Imported
    val imp : Imported = ImportedFactory.createImportedObject() 
}

这似乎是相当奇怪的行为。这对于在编译时具有类型推断的语言来说是正常的吗?由于我的无知,直到现在我在 go/C++ 中还没有注意到它?

两种有效方法(导入和显式类型与推断)中的一种是否比另一种具有优势/缺点? (当然,期待一个更明确和冗长,另一个更短)

这是黑魔法还是 Scala 编译器以一种相当直接的方式完成了这些推论?

【问题讨论】:

    标签: scala types casting jvm return-type-deduction


    【解决方案1】:

    导入唯一要做的就是在当前范围内提供一个不完全限定的名称。你也可以这样写:

    class App() {
        val imp: libraries.Imported = ImportedFactory.createImportedObject() 
    }
    

    您import libraries.Imported 的原因是为了让更短的名称Imported 可供您编写。如果让编译器推断类型,则无需在代码中提及类型,因此不必导入其较短的名称。

    顺便说一句:这与 C++ 中的动态转换无关。代码中唯一起作用的机制是类型推断。

    【讨论】:

    • 我误用了动态投射这个词,这可能确实对我的问题造成了相当多的混乱(并且缺乏建议),对不起。
    【解决方案2】:

    注意:使用术语类型推断

    ,您将获得更好的搜索结果

    使用val imp = ImportedFactory.createImportedObject(),您可以让编译器根据类型推断来确定imp 应该是什么类型。无论createImportObject 返回什么类型,imp 就是什么类型。

    使用val imp : Imported = ImportedFactory.createImportedObject(),您明确声明imp 是 Imported。但是编译器不知道你的意思,除非你......导入......它。

    这两种方法都有优点:

    推断类型

    推断类型非常适合将类型应该很明显的代码放在一起:

    val i = 1 // obviously `i` is an int
    val j = i + 10 // obviously still an int
    

    对于类型太难写的本地 vars/vals 来说也很棒

    val myFoo: FancyAbstractThing[TypeParam, AnotherTypeParam[OhNoMoreTypeParams]] = ...
    // vs
    val myFoo = FancyThingFactory.makeANewOne()
    

    缺点是,如果您允许公共 def/val 具有推断类型,则可能更难以确定如何使用该方法。出于这个原因,省略类型注释通常只用于简单的常量,并且在“客户端代码”不必查看的本地 vals/vars 中。

    显式类型

    当您确实想编写类似于库的代码(即公共 vals/defs)时,约定是显式键入它们。

    可能最简单的原因是因为:

    def myLibraryMethod = {
      // super complicated implementation
    }
    

    比

    更难理解
    def myLibraryMethod: String = {
      // super complicated implementation
    }
    

    显式键入代码的另一个好处是,当您想要公开比实际值更不具体的类型时:

    val invalidNumbers: Set[Int] = TreeSet(4, 8, 15, 16, 23, 42)
    

    在此示例中,您不希望客户端代码需要关心您的 invalidNumbers 实际上是 TreeSet。这是一个实现细节。在这种情况下,您隐藏了一些虽然真实但会分散注意力的信息。

    【讨论】:

      猜你喜欢
      • 2016-12-23
      • 1970-01-01
      • 2018-07-02
      • 2020-12-31
      • 1970-01-01
      • 1970-01-01
      • 2021-03-21
      • 2010-09-15
      • 1970-01-01
      相关资源
      最近更新 更多