Kotlin primary constructor calling secondary constructor - kotlin

Why does this not compile?
class test
{
constructor() {
var a = Date().day
this(a)
}
constructor(a:Int) {
}
}
error is:
Expression 'this' of type 'test' cannot be invoked as a function. The function 'invoke()' is not found.
The suggested fix is to add this:
private operator fun invoke(i: Int) {}
Why?

First, both of these constructors are secondary constructors. A primary constructor is one which is located outside of the body of the class.
Second, as described in the documentation, the correct syntax to call another constructor is as follows:
class Test {
constructor() : this(1) { }
constructor(a: Int) { }
}

class test constructor(){ // primary constructor (The primary constructor is part of the class header: it goes after the class name (and optional type parameters))
constructor(a: Int) : this() { // secondary constructor
}
}
If you class have define primary constructor, secondary constructor needs to delegate to the primary constructor. See here.
I think primary constructor can not be called from secondary constructor.
You can think like this: secondary calls primary and primary calls secondary => endless loop => not possible
In your case, there are 2 secondary constructor, so you can do like
class test {
constructor() : this(Date().day) // I see it quite like Java here https://stackoverflow.com/questions/1168345/why-do-this-and-super-have-to-be-the-first-statement-in-a-constructor
constructor(a: Int) {
}
}

Couple of things are wrong here:
Classes should always use camel-case for their names (test -> Test)
You cannot call another constructor as you tried to (calling this(1) inside of the other constructors body)
I think what you actually want is a being a property and alternatively initialize it with a default value. You could do it like this
class Test(val a: Int) {
constructor() : this(1) // notice how you can omit an empty body
}
or even better, like this:
class Test(val a: Int = 1) // again an empty body can be omitted.
Edit:
If you need to do some calculations, as asked in the comment below Yole's answer:
class Test(val day: Int) {
// you can use any expression for initialization
constructor(millis: Long) : this(Date(millis).day)
}
or if things get more complicated:
class Test(var day: Int) {
// pass something (i.e. 1) to the primary constructor and set it properly in the body
constructor(millis: Long) : this(1) {
// some code
day = // initialize day
}
}

Related

(Interface, Class) Kotlin: Expecting member declaration

Class Aim: read review point and comment, ensure that point is within 0-5
class LimitedReview(val point:Int, val comment:String):Review {
if (point<0){
point=0
}
if (point>5){
point = 5
}
override fun stars(): Int =point
override fun info(): String =comment
}
interface Review{
fun stars():Int
fun info():String
}
Error:(2, 5) Kotlin: Expecting member declaration
Error:(2, 17) Kotlin: Conflicting overloads: public final fun (): Unit defined in LimitedReview, public final fun (): Unit defined in LimitedReview
Error:(2, 17) Kotlin: Function declaration must have a name
may i know how to change my code?
which topic should i learn to avoid the same error again?
Thanks!
The property you have in the primary constructor of the class will assign the value that you call the constructor with to the point property directly, when your class is created, and you can't modify it any more.
Basically, this code:
class LimitedReview(val point: Int)
Is the same as this:
class LimitedReview(point: Int) {
val point: Int = point // ctor param to property
}
If you want to perform logic before assigning the value to the property, you have to move the property outside the constructor, and initialize it manually.
This can be done in an initializer block, if you have complex logic for it:
class LimitedReview(point: Int) {
val point: Int
init {
if (point < 0) {
this.point = 0
} else if (point > 5) {
this.point = 5
} else {
this.point = point
}
}
}
Or if you can fit it into a single expression (coerceIn comes in handy here), then inline with the property declaration:
class LimitedReview(point: Int) {
val point: Int = point.coerceIn(0..5)
}

How do I run code before calling the primary constructor?

I am writing a class that contains two immutable values, which are set in the primary constructor. I would like to add a secondary constructor that takes a string and parses it to get those two values. However, I can't figure out a way to implement this in Kotlin, as the secondary constructor calls the primary constructor immediately, before parsing the string.
In java, I would call this(a,b) in one of the other constructors, but Java doesn't have primary constructors. How do I add this functionality?
class Object (a: double, b:double)
{
val a = a
val b = b
constructor(str: String) //Parsing constructor
{
//Do parsing
a = parsed_a
b = parsed_b
}
}
You can either replace your parsing constructor with a factory method:
class Object(val a: Double, val b: Double) {
companion object {
// this method invocation looks like constructor invocation
operator fun invoke(str: String): Object {
// do parsing
return Object(parsed_a, parsed_b)
}
}
}
Or make both constructors secondary:
class Object {
val a: Double
val b: Double
constructor(a: Double, b: Double) {
this.a = a
this.b = b
}
// parsing constructor
constructor(str: String) {
// do parsing
a = parsed_a
b = parsed_b
}
}
Secondary constructors are disfavored in Kotlin. Your best solution is to use a factory method. See, e.g.:
class A(val a: Int, val b: Int) {
companion object {
fun fromString(str: String): A {
val (foo, bar) = Pair(1, 2) // sub with your parsing stuff
return A(foo, bar)
}
}
}
This will lead to more readable code. Imagine a class with ten different constructors identified no way other than MyClass as opposed to many more obvious ones enabled by the factory approach: MyClass.fromString(str: String) and MyClass.fromCoordinates(coordinates: Pair<Int, Int>) and so forth.
Secondary constructors weren't even allowed in Kotlin until relatively recently.

kotlin + aws lambda requiring empty constructor

I have the following class written in Kotlin+Guice that is invoked with a lambda
class LambdaProcessor #Inject constructor(private val s3Util: S3Util) {
fun lambdaInvokesThisMethod() {
s3Util.DoSomething()
}
}
That works great for unit testing, but lambda requires the class to have an empty constructor.
I can convert this same class to have an empty constructor by doing this:
class LambdaProcessor {
#Inject lateinit var s3Util: S3Util
init {
Guice.createInjector(GuiceDependencyInjector()).injectMembers(this)
}
fun lambdaInvokesThisMethod() {
s3Util.DoSomething()
}
}
That code now works great on lambda but I can't mock s3Util in my unit tests anymore because the init method gets called.
How can I get both scenarios to work together?
You can always declare secondary constructors. However all of them need to call primary constructor, so there's a trick to make it private and handle arguments in init block accordingly.
I don't use Guice but you could try something like this:
class LambdaProcessor private constructor(s3Util: S3Util?){
#Inject
lateinit var s3Util : S3Util
init{
s3Util?.let { this.s3Util = it }
}
// passes null to primary constructor, leaving field uninitalized
constructor() : this(null){
Guice.createInjector(GuiceDependencyInjector()).injectMembers(this)
}
// passes non-null to primary constructor initializing the field (cast to nullable needed to match primary signature)
constructor(s3Util: S3Util) : this(s3Util as S3Util?)
fun lambdaInvokesThisMethod() {
s3Util.DoSomething()
}
}
In Kotlin, if you have a default constructor then all your other constructors must call your default. But, you can have multiple constructors without a default constructor. Here is the final code we used.
class LambdaProcessor {
#Inject private lateinit var s3Util: S3Util
/**
* Lambda calls the no-arg constructor, use our IoC library to initialize our dependencies
*/
constructor() {
Guice.createInjector(GuiceDependencyInjector()).injectMembers(this)
}
/*
* Unit-testing constructor
*/
constructor(s3Util: S3Util) {
this.s3Util = s3Util
}
fun lambdaInvokesThisMethod() {
s3Util.DoSomething()
}
}

why one get "Platform declaration clash" but the other is fine?

Having a interface defined:
interface IData {
fun getHash() : Int
fun getUUID(): UUID
......
}
Trying to create object for the interface. the fun getUUID(): UUID is fine, but the fun getHash() : Int got error as below.
What might be wrong? why they are different?
fun buidlDataList () : ArrayList<IData> {
val dataList = ArrayList<IData>(0)
dataList.add(object : IData {
val hash: Int by lazy { dataFetchers.size+System.currentTimeMillis().toInt() } //<=== get error
override fun getHash(): Int { //<=== get the same error
return hash
}
val uuid: UUID by lazy { UUID.randomUUID() }
override fun getUUID(): UUID {
return uuid
}
......
}
}
Platform declaration clash: The following declarations have the same JVM signature(getHash() I):
* public final fun <get-hash>(): int defined in com.data. buidlDataList <no name provided>
* public open fun getHash(): int defined in defined in com.data. buidlDataList <no name provided>
The variables create their own getters, but you also explicitly define them. When you declare a var or a val, they usually have their own getters automatically generated1. Private vals or vars don't, if you don't create a custom getter.
But in all other cases, this:
val x: Int = TODO()
generates a getter1.
In your case, I'd recommend using val in the interface directly. You see, the generated getter has the same name as the getHash method you explicitly declared. The getters don't override methods either (unless you annotate it with one of the #Jvm annotations, and I don't remember which, but you don't really need those anyways).
So you change your interface to:
interface IData {
val hash: Int
val uuid: UUID
}
And remove the getters in the overridden object, and add override to the vals:
dataList.add(object : IData {
override val hash: Int by lazy { dataFetchers.size+System.currentTimeMillis().toInt() }
override val uuid: UUID by lazy { UUID.randomUUID() }
}
The first interface is actually equivalent to declaring an interface with get and set methods. If you override it from Java, it will ask you to override getHash() and getUid(), and you need to declare the field locally. Kotlin works differently because it automatically generates setters.
And since you can declare variables in interfaces without messing up Java interop, I highly recommend you use that over #Jvm* annotations (mainly because it makes it easier to understand the code, though that's a personal preference).
Also, if you decompile the Kotlin bytecode, you'll see what the interface with the variables compiles to:
public interface IData {
int getHash();
#NotNull
UUID getUuid();
}
So it's identical to the one you originally had, just without conflicts in child classes because of variable name clashes.
And the reason only one has a clash is because, as you see in the interface, val uuid creates a getter named getUuid, while your interface declares getUUID. Methods in Java and Kotlin are case-sensitive, which is why those don't clash. If you re-name your variable to upper-case UUID, you will get a clash on that too.
1: Assuming the variable/constant isn't in a method. Top-level variables, variables in interfaces, enums, classes, objects, and companion objects all generate getters/setters, but if you declare a variable inside a method, it naturaly won't have getters and setters where that applies.

Passing parameters to a custom getter in kotlin

I have been reading about properties in Kotlin, including custom getters and setters.
However, I was wondering if it is possible to create a custom getter with extra parameters.
For example, consider the following method in Java:
public String getDisplayedValue(Context context) {
if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
Note that the static method in PrefUtils has to have Context as a parameter, so removing this is not an option.
I would like to write it like this in Kotlin:
val displayedValue: String
get(context: Context) {
return if (PrefUtils.useImperialUnits(context)) {
// stuff
} else {
// other stuff
}
}
But my IDE highlights all of this in red.
I am aware I can create a function in my class to get the displayed value, but this would mean I would have to use .getDisplayedValue(Context) in Kotlin as well instead of being able to refer to the property by name as in .displayedValue.
Is there a way to create a custom getter like this?
EDIT: If not, would it be best to write a function for this, or to pass Context into the parameters of the class constructor?
As far as I know, property getter cannot have parameter. Write a function instead.
You can do this by having a property that returns an intermediate object that has a get and/or set operator with the parameters that you want, rather than returning the value directly.
Having that intermediate object be an inner class instance may be useful for providing easy access to the parent object. However, in an interface you can't use inner classes so in that case you might need to provide an explicit constructor parameter referencing the parent object when constructing your intermediate object.
For instance:
class MyClass {
inner class Foo {
operator fun get(context: Context): String {
return if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
}
val displayedValue = Foo()
}
...
val context : Context = whatever
val mc : MyClass = whatever
val y: String = mc.displayedValue[context]
You can do for example:
val displayedValue: String by lazy {
val newString = context.getString(R.string.someString)
newString
}