As far as I understand do (companion) objects not define types, while classes do. Therefore with objects implicit ClassTags cannot be used.
Is there a common way to create singletons for classes?
As far as I understand do (companion) objects not define types, while classes do. Therefore with objects implicit ClassTags cannot be used.
Is there a common way to create singletons for classes?
object
s define types. https://scastie.scala-lang.org/PXT1qWKdR9e3AxUDGcKU7Q :
import scala.reflect.ClassTag
object MyObject extends (String => Int) {
def apply(arg: String): Int =
arg.length
}
println(implicitly[ClassTag[MyObject.type]])
type MyType = MyObject.type
println(implicitly[ClassTag[MyType]])
So only companion objects do not define types?
They do, actually – folks generally just don’t think of them that way, since they tend to contain only pure functions. They’re mostly used as namespaces, but they are types.
Basically, any object
(created with that keyword) defines a class and the singleton instance of that class. They usually aren’t thought of as classes, but if you have an object named Foo
, you can get at its type by saying Foo.type
– it’s not terribly unusual to use that when you need to fill in a type parameter or suchlike.
Being a companion doesn’t change the type relation https://scastie.scala-lang.org/O4lKeuhsSjGdqzhLo4eKVg :
import scala.reflect.ClassTag
class MyType extends ((Int, Int) => Int) {
def apply(a: Int, b: Int): Int =
a + b
}
object MyType extends (String => Int) {
def apply(arg: String): Int =
arg.length
}
println(implicitly[ClassTag[MyType]])
println(implicitly[ClassTag[MyType.type]])
type MyClassType = MyType
type MyObjectType = MyType.type
println(implicitly[ClassTag[MyClassType]])
println(implicitly[ClassTag[MyObjectType]])
val classInstance: MyType = new MyType
val objectInstance: MyType.type = MyType
Notice that MyType
means different things when used as a value and when used as a type. MyType
when used as a value means instance of object MyType
. MyType
when used as a type (in type ascription) means class MyType
. You need to understand that types and values are two different and separate namespaces and they have different syntax.
I don’t understand why you say that objects define types, since this clearly does not work:
object Foo {}
val clazz = classOf[Foo]
And this does not work either:
object Foo {}
val clazz = classOf[Foo.type]
and
object Foo {}
val test: Foo = null
neither. It always fails with not found: type Foo
I believe, if I want to use single-tones that are defining a type, I need to do something like that:
class Single private()
object Single {
val instance = new Single()
}
val single: Single = Single.instance
Or do I miss something?
Types are a compile-time concept. Foo.type
is indeed a type, a full citizen of the universe of Scala types. Your val test: Foo = ...
should be val test: Foo.type = ...
.
java.lang.Class
is something else; that’s runtime reflection. See https://typelevel.org/blog/2017/02/13/more-types-than-classes.html
I don’t know why classOf[Foo.type]
is forbidden by the compiler. The following references show that we aren’t the first ones to wonder that, but they don’t resolve the question, either:
But anyway, if you are sure you want to do reflection anyway, you can Foo.getClass
(or Foo.getClass.asInstanceOf[Class[Foo.type]]
to get rid of the type bound).
Foo.getClass.asInstanceOf[Class[Foo.type]]
actually works but is not pleasant to look at, I have to say.
But what about my approach? It is not very Scala-like I suppose.
But what about my approach?
I don’t see an advantage?
The spec disallows this, Standard Library | Scala 2.13 defines classOf
only for class types, where class types are only those types that come from class or trait templates.
I can imagine that’s kind of useful for stability of the API if you want to be able to change implementation details of object
. Class types have a JVM class where the is a clear 1:1 relationship between the type, the class declaration and the JVM class. Objects (I mean those things initiated with object
, not instances of reference types) don’t have that. There is nothing (other than implementation details) that even guarantees that there is a single JVM class that implements the object
.
for
object Foo {
val someField = 7
def someMethod = someField.toString()
}
scalac generates two class files, Foo$.scala
and Foo.scala
Shown from javap:
$ javap -p -c -s Foo.class
Compiled from "Foo.scala"
public final class Foo {
public static java.lang.String someMethod();
descriptor: ()Ljava/lang/String;
Code:
0: getstatic #16 // Field Foo$.MODULE$:LFoo$;
3: invokevirtual #18 // Method Foo$.someMethod:()Ljava/lang/String;
6: areturn
public static int someField();
descriptor: ()I
Code:
0: getstatic #16 // Field Foo$.MODULE$:LFoo$;
3: invokevirtual #22 // Method Foo$.someField:()I
6: ireturn
}
$ javap -p -c -s Foo$.class
Compiled from "Foo.scala"
public final class Foo$ {
public static final Foo$ MODULE$;
descriptor: LFoo$;
private static final int someField;
descriptor: I
public static {};
descriptor: ()V
Code:
0: new #2 // class Foo$
3: dup
4: invokespecial #14 // Method "<init>":()V
7: putstatic #16 // Field MODULE$:LFoo$;
10: bipush 7
12: putstatic #18 // Field someField:I
15: return
public int someField();
descriptor: ()I
Code:
0: getstatic #18 // Field someField:I
3: ireturn
public java.lang.String someMethod();
descriptor: ()Ljava/lang/String;
Code:
0: aload_0
1: invokevirtual #24 // Method someField:()I
4: invokestatic #30 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
7: invokevirtual #33 // Method java/lang/Object.toString:()Ljava/lang/String;
10: areturn
private Foo$();
descriptor: ()V
Code:
0: aload_0
1: invokespecial #34 // Method java/lang/Object."<init>":()V
4: return
}
So you have an encoding where you have a JVM class Foo
with static methods that delegate to JVM class Foo$
which contains a static field for someField
, an instance accessor for someField
that delegates to a singleton instance method.
If scala could accept classOf[Foo.type]
, I suppose it could return what in java would be Foo$.getclass
, but that allows for a lot of mischief for questionable value. For example, you could create new instances of the singleton if you set the constructor to public, which may violate invariants in the language (which is worse than violating invariants in your application code).
If the object has a companion class, the jvm class implementing the class will also have the static forwarders to the companion. Maybe you can use that as a proxy?
You could also call out to java code, which will have no problem giving you a Foo$.getClass()
. You could return that (and cast if needed).
This sorrow would all disappear if you tried not to rely on reflection, which is difficult to deal with if squinting doesn’t help anymore in calling class declarations and instances, JVM classes and types “roughly the same thing” or different perspectives on the same thing. You can’t get away with that in scala to the extent you can in Java.
Can we maybe help you find a solution to the problem you’re trying to solve with reflection in a way that is a better fit for scala?
Thank you for the exhaustive answer!
A brief description of what I try to do:
class Builder[A] {
def build: A = ???
}
class Consumer {
val element: SomeClass = new Builder[SomeClass].build
}
This example, of course, does not make much sense, but it should explain what I want to do.
Now, the type SomeClass
cannot be an object
because I cannot use object
s with generics.
You can use the objects type Foo.type
in generics. You can’t use Foo.type do that with classOf
because classOf
is special (probably/maybe at least in part for reasons indicated).
The following compiles.
object Singleton
class Builder[A] {
def build: A = ???
}
class Consumer {
val element: Singleton.type = new Builder[Singleton.type].build
}
For some limits with the reflection approach: Scastie - An interactive playground for Scala.
The “general” solution is to punt the “problem” to the caller:
class Builder[A](construct: () => A) {
def build: A = construct()
}
though it’s questionable there is much more value in that than reducing the problem to
type Builder[A] = () => A
which is a different way to say – just use a () => A
instead of a Builder[A]
At the end of the day, you can’t construct values for all types. Some, because you don’t have a sensible “default” to construct. Others, because they’re simply uninhabited. You can’t get a Builder[Nothing]
that does something sensible, because Nothing
has no values. I’m sure you can construct other types that can’t have any values too.
If it were possible to write a general implementation of Builder for all types, with only a type parameter as input would violate that.
True, that works, thank you!
While using () => A
is better than reflection based shenanigans, there is an uniform way to get both class
and object
Class instance https://scastie.scala-lang.org/hUUDjCfZRk2Y6ec3n7cMRw :
import scala.reflect.ClassTag
class MyType extends ((Int, Int) => Int) {
def apply(a: Int, b: Int): Int =
a + b
}
object MyType extends (String => Int) {
def apply(arg: String): Int =
arg.length
}
println(classOf[MyType] eq implicitly[ClassTag[MyType]].runtimeClass)
println(MyType.getClass eq implicitly[ClassTag[MyType.type]].runtimeClass)
The uniform way is implicitly[ClassTag[...]].runtimeClass
, while classOf[...]
is working only for class
es and Singleton.getClass
is working only for object
s.