call constructor reference in Kotlin - class

If I have a kotlin class like this :
data class Anim (val name : String , var age : Int) {
constructor (a:Anim):this(a.name, a.age) {
}
constructor () :this("Dog") { }
}
and I want Use constructor reference syntax ,
val a = ::Anim
then I got this error :
overload resolution ambiguity:
public constructor PornModel() defined in com.ripple.PornModel
public constructor PornModel(a: PornModel) defined in com.ripple.PornModel
public constructor PornModel(name: String, country: String = ...)
defined in com.ripple.PornModel
val a = ::PornModel::( String, String))
Please tell me how special the arguments
Any help is much appreciated! Al

As stated in the error message Kotlin compiler does not know which constructor overload to pick. You have state the type of a explicitly e.g.:
val twoArgs: (String, Int) -> Anim = ::Anim
val oneArg: (Anim) -> Anim = ::Anim
val noArg: () -> Anim = ::Anim

Related

Kotlin – Why parameter values have to be passed in sub classes?

My code:
open class Club(name: String, country: String)
class FemaleClub(): Club()
var femaleClub = FemaleClub("Blue", "Australia")
Why is the above code not possible?
Why does it have the error
no value passed for parameter name
and
no value passed for parameter country
in my subclass? The values are set when I initiate femaleClub.
In your example parent class Club has primary constructor which is, by language specification, must be called either from secondary constructors of the same class or primary constructor of subclasses to initialize parameters defined in primary constructor. If you don't want to call a primary constructor of a parent class from subclasses you have a couple of options:
Set default values to parameters of primary constructor:
open class Club(name: String = "DefaultName", country: String = "Country") {
}
In this case you will not be able to call primary constructor with params:
// this is not possible
var femaleClub = FemaleClub("Blue", "Australia")
Create secondary constructor in parent class which calls primary constructor:
open class Club(name: String, country: String) {
constructor(): this("Name", "Country")
}
But also you won't be able to call FemaleClub("Blue", "Australia").
To be able to call constructor with parameters of a subclass FemaleClub("Blue", "Australia") you need explicitly define them in primary constructor of the subclass and call parent's primary constructor, i.e.:
class FemaleClub(name: String, country: String): Club(name, country) {}
There are some problems with your code:
FemaleClub does not have a constructor which accepts two arguments, even if the base class has one.
The primary constructor of the inherited class should call the primary constructor of the base class. Because your base class accepts two non-nullable arguments, you have to provide them to it, otherwise your code wont compile.
These issues can be fixed in the following way:
class FemaleClub(name: String, country: String): Club(name, country) {
}
About Kotlin classes and inheritance, read here Classes and Inheritance and here Kotlin Inheritance.
You can use at least four approaches to get what you want:
First approach (default values):
open class Club(var name: String = "Blue", var country: String = "Australia") { }
public class FemaleClub(): Club() { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: Australia
Second approach (passing values):
open class Club(var name: String, var country: String) { }
class FemaleClub(): Club("Green", "NZ") { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: NZ
Third approach (init block):
open class Club(name: String, country: String) {
var name: String = "Blue"
var country: String = "Australia"
init {
this.name = name
this.country = country
}
}
class FemaleClub(): Club("Green", "NZ") { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: NZ
Fourth approach (class sec.constructor):
open class Club {
var name: String
var country: String
constructor(name: String = "Blue", country: String = "Australia") {
this.name = name
this.country = country
}
}
class FemaleClub(): Club() { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: Australia
In FemaleClub you are not specifying what should be passed to Club as its 2 String arguments, name and country.
And you are not specifying anywhere that FemaleClub takes 2 strings as constructor arguments, so the last line wont work either.
You need to invoke the constructor (primary/secondary) from the class you want to inherit from.
Club only has one constructor, the primary constructor. It takes two strings as parameters. Club does not have an empty constructor.
So, it cannot be invoked as Club().
I would propose two changes.
1) Make name and country of Club properties
open class Club(name: String, country: String)
2) Declare parameters in FemaleClub's primary constructor
Since you want to be able to specify values for name and country when you instantiate FemaleClub it would be a good idea to give the primary constructor of FemaleClub name and country as parameters.
Additionally I would recommend using named parameters when passing on the values, since you can easily mix up the two strings (resulting in passing name as country and country as name.
class FemaleClub(name: String, country: String): Club(name = name, country = country)

How do I correct bad arguments to a Scala constructor?

I have a Scala class as shown:
class MyClass(title: String, value: Int) {
...
}
If the constructor is called with a title value of null, I would like to set title to the empty string instead. How can I do that? Is there a cleaner way than forcing title to be private and providing a getter?
def getTitle: String = if (title == null) "" else title
You can create a factory method that provides the desired value. Often, in Scala, this is done in the companion object:
object MyClass {
def apply( title: String, value: Int ): MyClass =
new MyClass( if (title == null) "" else title, value)
}
As it stands your title value is just a constructor parameter so it is not accessible from outside (did you omit a val?). You can use this fact to compute the real title member like this:
class MyClass(_title: String, val value: Int) {
val title = if (_title == null) "" else _title
...
}
This guarantees that title is not null in any instance of MyClass
For completeness, here is the alternative factory method implementation:
trait MyClass {
def title: String
def value: Int
}
object MyClass {
protected class MyClassImplementation(val title: String, val value: Int) extends MyClass {}
def apply(title: String, value: Int) =
new MyClassImplementation(if (title == null) "" else title, value)
}
The only way to create an instance of MyClass is via the factory method, so the null check is always called.
Instead of using nullable variables that requires constant checking if-not-null, Scala encourages you to use Option where a value can be "none".
One way of achieving this is to use a secondary-constructor:
class ClassX(title: Option[String]) {
def this(title: String) {
this(Option(title))
}
}
And if you have to use a nullable variable, you could use the factory mentioned above.

How to initialize a field by calling a method

The following class refuses to compile:
class InitTest { // Class 'InitTest' must either be declared abstract
// or implement abstract member 'v: Int'
var v: Int
def int(v : Int) = {
this.v = v
}
}
I was kind of surprise by that we can't just leave values "uninitialized". In Java, it would be assigned with null. In Scala, it does not compile. How to do this in Scala?
You can do this:
class InitTest {
var v: Int = _
def int(v : Int) = {
this.v = v
}
}
Since v has a value type, there is no way of assigning null to it. However, Scala lets you use _ to represent the "zeroed" value. For numbers, that is zero and for pointers that is null. Good way of representing uninitialized values.

Reference method from different class as curried function

There are two merge methods in RACSignal:
- (RACSignal *)merge:(RACSignal *)signal;
+ (RACSignal *)merge:(id<NSFastEnumeration>)signals;
When I write RACSignal.merge it references static method:
class func merge(signals: NSFastEnumeration!) -> RACSignal!
How to reference object method? I can't write self.merge, because it is in wrapper class and self is not RACSignal.
The curried class function and the curried instance function have
different signatures. Similarly as in
Swift - get reference to a function with same name but different parameters
you can refer to each by specifying the signature explicitly.
I have no experience with RACSignal, so here is an artificial
example that hopefully can be applied in your case:
class MyClass {
class func foo(s : String) {
println("class foo: \(s)")
}
func foo(s : String) {
println("instance foo: \(s)")
}
}
// Reference to the class method:
let f1 : String -> Void = MyClass.foo
// Call the class method:
f1("bar")
// Output: class foo: bar
// Reference to the instance method:
let f2 : MyClass -> String -> Void = MyClass.foo
// Call the instance method:
let obj = MyClass()
f2(obj)("bar")
// Output: instance foo: bar
As methods in Swift are curried class functions, compiler has to decide which overload to choose.
To reference instance's merge method you need to specify it's exact type:
let instanceMerge: RACSignal -> RACSignal! -> RACSignal! = RACSignal.merge

Getting an argument from a private constructor

I want to get access to an argument in a private constructor without using mutable variables:
class Class1 {
val strArgPublic = // get strArg argument from the private constructor. How?
private def this(strArg: String) = {
//.....
}
}
I want not only get strArg and return it, but change it a little bit and return a new modified copy of it.
How can I do this?
There is not only private constructor in your class. There is also a public constructor. You should decide what will be a value of strArgPublic after public constructor. If there is should be no public constructor you should define your class like this:
class Class1 private(strArg: String) {
val strArgPublic = transform(strArg)
}
If there should be a parameterless public constructor you could define one as auxiliary constructor:
class Class1 private(strArg: String) {
val strArgPublic = transform(strArg)
def this() = this("default")
}