是否需要一个类的伴随对象(单例)?为什么我要创建一个类,比如说Foo
并为它创建一个伴随对象?
在Scala中,如果我定义一个apply
在类或顶级对象中调用的方法,那么每当我将一个圆括号添加到该类的实例时,将调用该方法,并apply()
在它们之间放置适当的参数.例如:
class Foo(x: Int) {
def apply(y: Int) = {
x*x + y*y
}
}
val f = new Foo(3)
f(4) // returns 25
Run Code Online (Sandbox Code Playgroud)
所以基本上,object(args)
只是语法糖object.apply(args)
.
Scala如何进行此转换?
这里是否存在全局定义的隐式转换,类似于Predef对象中的隐式类型转换(但实物类型不同)?或者它是一些更深层次的魔法?我问,因为看起来Scala强烈赞成一套较小的规则的一致应用,而不是许多例外的许多规则.这最初对我来说似乎是个例外.
kotlin中一个对象和一个伴侣对象有什么区别?
例:
class MyClass {
object Holder {
//something
}
companion object {
//something
}
}
Run Code Online (Sandbox Code Playgroud)
我已经读过,如果包含的参数/方法与其类密切相关,则应使用伴随对象.
但为什么还有可能在类中声明一个普通的对象?因为它的行为与伴侣完全相同,但它必须具有名称.
它的"静态"(我来自java方面)生命周期可能有所不同吗?
当你创建一个类的情况下,编译器创建了几个案例类东西的相应配套对象:一个apply
工厂方法相匹配的主构造,equals
,hashCode
,和copy
.
奇怪的是,这个生成的对象扩展了FunctionN.
scala> case class A(a: Int)
defined class A
scala> A: (Int => A)
res0: (Int) => A = <function1>
Run Code Online (Sandbox Code Playgroud)
只有在以下情况下才会这样:
有没有人使用它,或者知道它为什么被添加?它使用静态转发器方法稍微增加了生成的字节码的大小,并显示在#toString()
伴随对象的方法中:
scala> case class A()
defined class A
scala> A.toString
res12: java.lang.String = <function0>
Run Code Online (Sandbox Code Playgroud)
UPDATE
使用单个apply
方法手动创建的对象不会自动视为FunctionN
:
object HasApply {
def apply(a: Int) = 1
}
val i = HasApply(1)
// fails
// HasApply: (Int => …
Run Code Online (Sandbox Code Playgroud) 我是第一次教斯卡拉,我的学生们发现故意"惩罚"涉及伴侣物体非常混乱.请考虑以下示例:
class Stack {
... methods such as push/pop
}
object Stack {
... factory method(s) and possibly others
}
Run Code Online (Sandbox Code Playgroud)
当我使用诸如"堆栈对象"或"堆栈对象"之类的语言短语或尤其是"堆栈对象"时,会出现混淆.我的学生很难理解我是指单身对象Stack还是Stack类的对象.
我正在寻找替代方法来表达这些初学者可以更容易理解的东西.我一直认为Stack类的对象总是称为"Stack实例"或"Stack的实例",但是当试图教OO不能称这些东西为对象时,它似乎很疯狂.在谈论单例对象Stack时,我一直试图总是使用短语"singleton object"或"companion object",但Scala的语法在那里对我不利,因为它只使用了"object"这个词.
在这种情况下,我可以重命名单例对象StackFactory而不是Stack,但这只是我自己的类的一个选项,而不是已经内置到Scala中的一千个和一个伴随对象.
编辑:
对不起,我的问题不够清楚.主要的混淆不是在引用伴侣对象时发生的.在这种情况下,正如几个人所指出的那样,很容易使用诸如"伴侣对象"之类的短语.相反,在引用普通实例时会发生主要的混淆.然后,如果我说"堆栈对象"(意思是一些堆栈实例)或"堆栈对象"(意味着这个特定实例),一些学生会认为我的意思是伴侣对象 - 即使我没有使用单词伴侣或单身人士.
我可以很好地看到混淆来自哪里,因为"对象"这个词只出现在程序文本中,只出现在伴侣对象中.
我需要两个可以访问彼此私有的实例.我天生就想到了一个伴侣对象,它允许访问它的同伴类的唯一实例.这个类本身是私有的,因此用户不能只使用创建实例new
.
object A {
def apply = dual
lazy val dual = new A
}
private class A {
//some irrelevant logic...
}
Run Code Online (Sandbox Code Playgroud)
此代码无法编译.我得到:类A逃避其定义范围作为类型A错误的一部分,我真的不明白.我当前的解决方法是使用类应该具有的每个方法声明来定义特征并使class A
该特性扩展,而双重属于特征类型,而不是class A
类型.
我在这里缺少什么理论问题?为什么这是禁止的?
我尝试编写一个类来管理 SQLite 数据库,但出现错误消息“需要顶级声明 > 任务 :app:buildInfoGeneratorDebug”。
package com.xexxxwxxxxs.GMP
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.content.Context
import android.content.ContentValues
class DBHandler(context: Context, name: String?, factory: SQLiteDatabase.CursorFactory?, version: Int) : SQLiteOpenHelper(context, DATABASE_NAME, factory, DATABASE_VERSION)
{
override fun onCreate(db: SQLiteDatabase)
{
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int)
{
}
companion object
{
private val DATABASE_VERSION = 1
private val DATABASE_NAME = "GMP.db"
}
}?
Run Code Online (Sandbox Code Playgroud)
你有什么想法?
提前致谢
Scala类的"伴随对象"可以被视为单个对象,具有与该类相同的完全限定名称(即同一个名称,在同一个包中).它们用于保存类的所有实例共有的实用程序函数,以替代Java的static
方法.
但是,在文档和问题的不同位置,它表示必须在同一编译单元中定义伴随对象.例如,它们必须在同一个文件中定义; 无法为Java对象定义伴随对象 ; 在REPL中,它们必须在同一输入行上定义,因此警告消息:
warning: previously defined class Foo is not a companion to object Foo.
Companions must be defined together; you may wish to use :paste mode for this.
Run Code Online (Sandbox Code Playgroud)
这意味着必须区分具有其伴随对象的类,以及具有相同(完全限定)名称的类和对象.这是什么区别?
oop scala language-lawyer scala-java-interop companion-object
定义case类时,默认的伴随对象有一个很好的curried
方法来获取case类构造函数的curried版本:
scala> case class Foo(a: String, b: Int)
defined class Foo
scala> Foo.curried
res4: String => (Int => Foo) = <function1>
Run Code Online (Sandbox Code Playgroud)
但是,只要我定义了一个显式的伴随对象,这个方法就会消失:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo(a: String, b: Int)
object Foo {}
// Exiting paste mode, now interpreting.
defined class Foo
defined module Foo
scala> Foo.curried
<console>:9: error: value curried is not a member of object Foo
Foo.curried
Run Code Online (Sandbox Code Playgroud)
我可以这样回来:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class …
Run Code Online (Sandbox Code Playgroud) 我已经在Kotlin中编写了这些方法并分析了字节码:
class A {
object b {
fun doSomething() {}
}
}
Run Code Online (Sandbox Code Playgroud)
class A {
companion object b {
fun doSomething() {}
}
}
Run Code Online (Sandbox Code Playgroud)
fun doSomething() {}
Run Code Online (Sandbox Code Playgroud)
字节码结果
Test$asb
,public final doSomething()I
Test$Companion
,public final doSomething()I
TestKt
,public final static doSomething()I
我的问题是:
我有一个枚举类,我想在给定枚举变量的情况下返回一个枚举实例,例如findById (enum(id, color))
.我该怎么办?伴侣对象?宾语?
似乎有一个真正的静态方法的唯一方法是在包级别,没有类声明.但这有点过于全球化.有没有办法通过以下方式访问它:ClassName.staticMethod
,staticMethod是非常静态的.
提供有意义的包声明方法,伴随对象和对象的示例.
语境.我一直在Kotlin编码,我发现它很棒.但有时我需要做出决定:例如,一个沉重的不可变属性在java中我会声明为静态final,但在Kotlin中我发现很难"找到一个等价物".