I want to do something like this:
interface Serializable<FromType, ToType> {
fun serialize(): ToType
companion object {
abstract fun deserialize(serialized: ToType): FromType
}
}
or even this would work for me:
interface Serializable<ToType> {
fun serialize(): ToType
constructor(serialized: ToType)
}
but neither compiles. Is there a syntax for this, or will I be forced to use make this an interface for a factory? Or is there another answer? 😮 That'd be neat!
Basically, nothing in a companion object can be abstract or open (and thus be overridden), and there's no way to require the implementations' companion objects to have a method or to define/require a constructor in an interface.
A possible solution for you is to separate these two functions into two interfaces:
interface Serializable<ToType> {
fun serialize(): ToType
}
interface Deserializer<FromType, ToType> {
fun deserialize(serialized: ToType): FromType
}
This way, you will be able to implement the first interface in a class and make its companion object implement the other one:
class C: Serializable<String> {
override fun serialize(): String = "..."
companion object : Deserializer<C, String> {
override fun deserialize(serialized: String): C = C()
}
}
Also, there's a severe limitation that only a single generic specialization of a type can be used as a supertype, so this model of serializing through the interface implementation may turn out not scalable enough, not allowing multiple implementations with different ToTypes.
For future uses, it's also possible to give the child class to a function as a receiver parameter:
val encodableClass = EncodableClass("Some Value")
//The encode function is accessed like a member function on an instance
val stringRepresentation = encodableClass.encode()
//The decode function is accessed statically
val decodedClass = EncodableClass.decode(stringRepresentation)
interface Encodable<T> {
fun T.encode(): String
fun decode(stringRepresentation: String): T
}
class EncodableClass(private val someValue: String) {
// This is the remaining awkwardness,
// you have to give the containing class as a Type Parameter
// to its own Companion Object
companion object : Encodable<EncodableClass> {
override fun EncodableClass.encode(): String {
//You can access (private) fields here
return "This is a string representation of the class with value: $someValue"
}
override fun decode(stringRepresentation: String): EncodableClass {
return EncodableClass(stringRepresentation)
}
}
}
//You also have to import the encode function separately:
// import codingProtocol.EncodableClass.Companion.encode
This is the more optimal use case for me. Instead of one function in the instanced object and the other in the companion object like your example, we move both functions to the companion object and extend the instance.
Related
I use Kotlin and Mongo (with KMongo) and I have multiple models as UserEntity, MovieEntity and so on. Each of them use a specific Dao class to do (actually) the same methods. Therefore, I'm trying to avoid any duplication by using a BaseDao which should have these methods instead.
So I pass the specific entity in the generic base as:
class UserDao : BaseDao<UserEntity>() { ... }
This base class implements the generic methods as follows:
open class BaseDao<T: Any>() {
fun get(id: String): T? {
return getCollection().findOneById(id)
}
fun save(entity: T): T {
return getCollection().save(entity)
}
fun delete(id: String) {
getCollection().deleteOneById(id)
}
...
}
However, a problem occurs on getCollection() method:
private inline fun <reified T: Any> getCollection(): MongoCollection<T> {
return MongoDb.getDatabase().getCollection<T>()
}
This gets a compilation error each time I call it:
Type inference failed: Not enough information to infer parameter T in
inline fun <reified T : Any> getCollection(): MongoCollection<T#1 (type parameter of app.api.db.dao.BaseDao.getCollection)>
Please specify it explicitly.
I can't find the right way to do this. I already checked these threads but I didn't make it work: Generic class type usage in Kotlin & Kotlin abstract class with generic param and methods which use type param.
Question:
How can I achieve this generic BaseDao which should get any collection of each child Dao?
the JVM forgets the type of the generic T in BaseDao<T: Any>() at runtime, which is why type inference fails. A solution to this could be to pass the KClass of T in the constructor of BaseDao:
open class BaseDao<T: Any>(val kClass: KClass<T>) {
...
}
After this, give your reified function an argument that accepts a `KClass:
private inline fun <reified T: Any> getCollection(val kClass: KClass<T>): MongoCollection<T> {
return MongoDb.getDatabase().getCollection<T>()
}
I'm unaware of a method to do this without passing the KClass as a argument to the function, but this should work, as the generic T can be derived from the provided kClass.
`
Another way would be to make all methods in BaseDao inline function with reified generics and dropping the generic on the class.
open class BaseDao() {
inline fun <reified T: Any> get(id: String): T? {
return getCollection().findOneById(id)
}
inline fun <reified T: Any> save (entity: T): T {
return getCollection().save(entity)
}
inline fun <reified T: Any> delete(id: String) {
getCollection().deleteOneById(id)
}
...
}
This way the generic T can be derived since the method calling getCollection is also reified.
(For KMongo 4.0.+) no need to use reified generics for the each method, instead this base class can be used as as a starting point:
open class BaseDao<T: Any>(
protected val collection: CoroutineCollection<T>
) {
suspend fun get(id: Id<T>): T? {
return collection.findOneById(id)
}
suspend fun save(entity: T): UpdateResult? {
return collection.save(entity)
}
suspend fun delete(id: Id<T>) {
collection.deleteOneById(id)
}
}
And implemented in the particular DAO, say SessionDao:
class SessionDao(collection: CoroutineCollection<DbSession>)
: BaseDao<DbSession>(collection)
(note: inheritance can be replaced with delegation by using by keyword if one feel better this way
This and other dao can be created via DI or some sort of dao factory:
class DbInstance(mongodbConnectionString: String = "mongodb://localhost:27017/myproject") {
private val connectionInfo = ConnectionString(mongodbConnectionString)
val client = KMongo.createClient().coroutine
val db = client.getDatabase(
connectionInfo.database ?: throw IllegalArgumentException("mongodb connection string must include db name")
)
val sessions = SessionDao(db.getCollection())
}
Notes:
This example is for the coroutine based kmongo, it can be easly converted to blocking kmongo by replacing CoroutineCollection to MongoCollection
I assume documents id's are annotated via Id container which helps to mitigate errors, so documents should be created in this fashion:
data class DbSession(
#BsonId
val id: Id<DbSession>,
val name: String,
)
The solution is to use reflection as Zigzago mentioned by using KMongoUtil:
protected fun getCollection(): MongoCollection<T> =
getDaoEntityClass().let { k ->
MongoDb.getDatabase().getCollection(
KMongoUtil.defaultCollectionName(k), k.java)
}
#Suppress("UNCHECKED_CAST")
private fun getDaoEntityClass(): KClass<T>
= ((this::class.java.genericSuperclass
as ParameterizedType).actualTypeArguments[0] as Class<T>).kotlin
I have a class
class MyClass {
def apply(myRDD: RDD[String]) {
val rdd2 = myRDD.map(myString => {
// do String manipulation
}
}
}
object MyClass {
}
Since I have a block of code performing one task (the area that says "do String manipulation"), I thought I should break it out into its own method. Since the method is not changing the state of the class, I thought I should make it a static method.
How do I do that?
I thought that you can just pop a method inside the companion object and it would be available as a static class, like this:
object MyClass {
def doStringManipulation(myString: String) = {
// do String manipulation
}
}
but when I try val rdd2 = myRDD.map(myString => { doStringManipulation(myString)}), scala doesn't recognize the method and it forces me to do MyClass.doStringManipulation(myString) in order to call it.
What am I doing wrong?
In Scala there are no static methods: all methods are defined over an object, be it an instance of a class or a singleton, as the one you defined in your question.
As you correctly pointed out, by having a class and an object named in the same way in the same compilation unit you make the object a companion of the class, which means that the two have access to each others' private fields and methods, but this does not mean they are available without specifying which object you are accessing.
What you want to do is either using the long form as mentioned (MyClass.doStringManipulation(myString)) or, if you think it makes sense, you can just import the method in the class' scope, as follows:
import MyClass.doStringManipulation
class MyClass {
def apply(myRDD: RDD[String]): Unit = {
val rdd2 = myRDD.map(doStringManipulation)
}
}
object MyClass {
private def doStringManipulation(myString: String): String = {
???
}
}
As a side note, for the MyClass.apply method, you used the a notation which is going to disappear in the future:
// this is a shorthand for a method that returns `Unit` but is going to disappear
def method(parameter: Type) {
// does things
}
// this means the same, but it's going to stay
// the `=` is enough, even without the explicit return type
// unless, that is, you want to force the method to discard the last value and return `Unit`
def method(parameter: Type): Unit = {
// does things
}
You should follow scala's advice.
val rdd2 = myRDD.map(MyClass.doStringManipulation)
Write this inside the class then it will work as expected.
import MyClass._
I am looking for the cleanest way to allow user to choose implementation of a method without repeating myself. in the situation below, each of the subclasses put together a greeting in XML with parameters from the specific class. thus the method toXML is declared abstract in the trait. What I want, however is to check if a _generalMessage was passed in in the construction of the class, and if so, use a general XML greeting common to all implementations of Greeting, e.g. <Message>_generalMessage</Message>. I know I can just pattern match on the existence of _generalMessage in each of the implementations of Greeting, but I am curious if there is a more elegant way.
trait Greeting {
protected var foo = //...
protected var _generalMessage: Option[Srting] = None
//...
//public API
def generalMessage: String = _generalMessage match {case Some(x) => x; case None =>""
def generalMessage_=(s: String) {_generalMessage = Some(s)}
protected def toXML: scala.xml.Node
}
class specificGreeting1 extends Greeting {
// class implementation
def toXML: scala.xml.Node = <//a detailed XML with values from class specificGreeting1>
}
// multiple other specificGreeting classes
Make toXML final, and define it in the base trait:
final def toXML = _generalMessage.fold(specific message) { m =>
<Message>m</Message>
}
Then define specificMessage in your subclasses to be what you currently have as toXML.
Could someone explain why scala would allow a public variable, to satisfy the implementation of an abstract declared Protected item? My first assumption is that the compiler would complain, but I created a small test to see if this worked, and to my surprise it does. Is there an advantage to this? (perhaps this is normal in OOP?) Any methods to avoid the accidental pitfall?
object NameConflict extends App {
abstract class A {
protected[this] var name:String
def speak = println(name)
}
class B(var name:String) extends A { //notice we've declared a public var
}
val t = new B("Tim")
t.speak
println(t.name) // name is exposed now?
}
It's normal and as in Java. Sometimes it's desirable to increase the visibility of a member.
You can't do it the other way around and turn down visibility in a subclass, because the member can by definition be accessed through the supertype.
If invoking a method has terrible consequences, keep the method private and use a template method that can be overridden; the default implementation would invoke the dangerous method.
abstract class A {
private[this] def dangerous = ???
final protected def process: Int = {
dangerous
template
}
protected def template: Int = ???
}
class B extends A {
override def template = 5
}
Assume I want to offer method foo on existing type A outside of my control. As far as I know, the canonical way to do this in Scala is implementing an implicit conversion from A to some type that implements foo. Now I basically see two options.
Define a separate, maybe even hidden class for the purpose:
protected class Fooable(a : A) {
def foo(...) = { ... }
}
implicit def a2fooable(a : A) = new Fooable(a)
Define an anonymous class inline:
implicit def a2fooable(a : A) = new { def foo(...) = { ... } }
Variant 2) is certainly less boilerplate, especially when lots of type parameters happen. On the other hand, I think it should create more overhead since (conceptually) one class per conversion is created, as opposed to one class globally in 1).
Is there a general guideline? Is there no difference, because compiler/VM get rid of the overhead of 2)?
Using a separate class is better for performance, as the alternative uses reflection.
Consider that
new { def foo(...) = { ... } }
is really
new AnyRef { def foo(...) = { ... } }
Now, AnyRef doesn't have a method foo. In Scala, this type is actually AnyRef { def foo(...): ... }, which, if you remove AnyRef, you should recognize as a structural type.
At compile time, this time can be passed back and forth, and everywhere it will be known that the method foo is callable. However, there's no structural type in the JVM, and to add an interface would require a proxy object, which would cause some problems such as breaking referential equality (ie, an object would not be equal with a structural type version of itself).
The way found around that was to use cached reflection calls for structural types.
So, if you want to use the Pimp My Library pattern for any performance-sensitive application, declare a class.
I believe 1 and 2 get compiled to the same bytecode (except for the class name that gets generated in case 2).
If Fooable exists only for you to be able to convert implicitly A to Fooable (and you're never going to directly create and use a Fooable), then I would go with option 2.
However, if you control A (meaning A is not a java library class that you can't subclass) I would consider using a trait instead of implicit conversions to add behaviour to A.
UPDATE:
I have to reconsider my answer. I would use variant 1 of your code, because variant 2 turns out to be using reflection (scala 2.8.1 on Linux).
I compiled these two versions of the same code, decompiled them to java with jd-gui and here are the results:
source code with named class
class NamedClass { def Foo : String = "foo" }
object test {
implicit def StrToFooable(a: String) = new NamedClass
def main(args: Array[String]) { println("bar".Foo) }
}
source code with anonymous class
object test {
implicit def StrToFooable(a: String) = new { def Foo : String = "foo" }
def main(args: Array[String]) { println("bar".Foo) }
}
compiled and decompiled to java with java-gui. The "named" version generates a NamedClass.class that gets decompiled to this java:
public class NamedClass
implements ScalaObject
{
public String Foo()
{
return "foo";
}
}
the anonymous generates a test$$anon$1 class that gets decompiled to the following java
public final class test$$anon$1
{
public String Foo()
{
return "foo";
}
}
so almost identical, except for the anonymous being "final" (they apparently want to make extra sure you won't get out of your way to try and subclass an anonymous class...)
however at the call site I get this java for the "named" version
public void main(String[] args)
{
Predef..MODULE$.println(StrToFooable("bar").Foo());
}
and this for the anonymous
public void main(String[] args) {
Object qual1 = StrToFooable("bar"); Object exceptionResult1 = null;
try {
exceptionResult1 = reflMethod$Method1(qual1.getClass()).invoke(qual1, new Object[0]);
Predef..MODULE$.println((String)exceptionResult1);
return;
} catch (InvocationTargetException localInvocationTargetException) {
throw localInvocationTargetException.getCause();
}
}
I googled a little and found that others have reported the same thing but I haven't found any more insight as to why this is the case.