There are can be two ways of writing helper method in Kotlin
First is
object Helper {
fun doSomething(a: Any, b: Any): Any {
// Do some businesss logic and return result
}
}
Or simply writing this
fun doSomething(a: Any, b: Any): Any {
// Do some businesss logic and return result
}
inside a Helper.kt class.
So my question is in terms of performance and maintainability which is better and why?
In general, your first choice should be top-level functions. If a function has a clear "primary" argument, you can make it even more idiomatic by extracting it as the receiver of an extension function.
The object is nothing more than a holder of the namespace of its member functions. If you find that you have several groups of functions that you want to categorize, you can create several objects for them so you can qualify the calls with the object's name. There's little beyond this going in their favor in this role.
object as a language feature makes a lot more sense when it implements a well-known interface.
There's a third and arguably more idiomatic way: extension functions.
fun Int.add(b: Int): Int = this + b
And to use it:
val x = 1
val y = x.add(3) // 4
val z = 1.add(3) // 4
In terms of maintainability, I find extension functions just as easy to maintain as top-level functions or helper classes. I'm not a big fan of helper classes because they end up acquiring a lot of cruft over time (things people swear we'll reuse but never do, oddball variants of what we already have for special use cases, etc).
In terms of performance, these are all going to resolve more or less the same way - statically. The Kotlin compiler is effectively going to compile all of these down to the same java code - a class with a static method.
Related
Some programming languages have the inline or other keyword to manual specify a function call site to be replaced with the body of the called function.
C# for example does not have this, because the compiler automatically decides which code gets inlined, avoiding, in my opinion, polluting the developer experience (developers shouldn't be worrying about optimizations).
Some languages implemented a syntax to inline classes like Kotlin and now Dart, which wrap an existing type into a new static type, reducing the overhead of a tradicional class.
Dart declaration example (specificated, not yet implemented):
inline class Foo {
// A single instance variable, defining the representation type.
final Bar bar;
// The desired set of other members.
void function1() {
bar.baz;
}
...
}
My question is, could a compiler make this optimization automatically in classes? If not, what challenges make this difficult/impossible?
It is not only about optimisation. Some inlining could make the resultant code less performant and/or larger, so Kotlin gives you control. (IntelliJ warnings against inlining in some cases - warning you that it won't improve performance.)
More than that, you should read about Reified Type Parameters - this allows for certain coding techniques that are only possible when the function is inlined as well as the type information.
Here is some code that is impossible in Java:
Suppose you have a series of enums, representing states of an Object, e.g.
enum class Color {RED,BLUE,GREEN}
enum class Size {SMALL,MEDIUM,LARGE}
data class MyObject(val color: Color, val size:Size)
and you had a test data generator that uses an Random number generator to pick a random enum for the Object.
In Kotlin you can write:
val rnd = Random(1)
val x = MyObject(
color = getRandomEnum(rnd),
size = getRandomEnum(rnd),
)
Using this
private inline fun <reified T : Enum<T>> getRandomEnum(rnd: Random): T {
val values: Array<T> = enumValues()
return values.get(rnd.nextInt(values.size))
}
I wonder if a data class with one of the properties being a function, such as:
data class Holder(val x: Data, val f: () -> Unit)
can work at all, since the following test fails.
val a = {}
val b = {}
Assert.assertEquals(a, b)
Update: Use case for this could be to have a
data class ButtonDescriptor(val text: String, val onClick: () -> Unit)
and then flow it to UI whilst doing distinctUntilChanged()
I don't think this is possible, I'm afraid.
You can of course check reference equality (===, or == in this case because functions don't generally override equals()). That would give you a definite answer where you have references to the same function instance. But that doesn't check structural equality, and so reports the two lambdas in the question as different.
You can check whether the two functions are instances of the same class by checking their .javaClass property. If the same, that would imply that they do the same processing — though I think they could still have different variables/captures. However, if different, that wouldn't tell you anything. Even the simple examples in the question are different classes…
And of course, you can't check them as ‘black boxes’ — it's not feasible to try every possible input and check their outputs. (Even assuming they were pure functions with no side effects, which in general isn't true!)
You might be able to get their bytecode from a classloader, and compare that, but I really wouldn't recommend it — it'd be a lot of unnecessary work, you'd have to allow for the difference in class names etc., it would probably have a lot of false negatives, and again I think it could return the same code for two functions which behaved differently due to different parameters/captures.
So no, I don't think this is possible in JVM languages.
What are you trying to achieve with this, and could there be another way? (For example, if these functions are under your control, can you arrange for reference equality to do what you need? Or could you use function objects with an extra property giving an ID or something else you could compare?)
When you create your data class, if you pass the function by reference it will work with DiffUtils and distinctUntilChanged(). Function references do not break the isEquals() method of data classes in the same way that a lambda does.
For example, you create a function for your onClick:
private fun onClick() { // handle click }
and create your data class like
BottomDescriptor("some text", ::onClick)
I have a class Track which holds a set of Points and represent person location in time. In order to get this result I run an iterative optimization routine combining different data. In order to do it I extend Point with a class OptimizedPoint which holds data for optimization for this point and current value. I also introduce OptimizedTrack which is a collection of OptimizedPoints and additional data needed for optimization associated with the whole track.
I run an optimization on OptimizedTrack and at the last iteration, I return to the user clean data (Track) which only has the result and doesn't have additional data. However, I can not find a way to express with OOP that OptimizedTrack is somehow an extension of the Track and introduce common routines for them. F.e getting a length of the track which should be available for both of them as it only uses data which can be found both in OptimizedTrack and Track
Right now I have such architecture Point{x, y, z}, OptimizedPoint extends Point {additional_data}. Track {array<Point>}, OptimizedTrack {array<OptimizedPoint>, additional_data}. I don't understand how to express that OptimizedTrack is an extension of Track as I can not express array<OptimizedPoint> extens array<Point>. So I can not introduce a common routine length which can be calculated for array and therefore also from array.
I do not insist on my current architecture. It's most probably wrong, I only write it here to express the problem I am facing. How would you propose to refactor my architecture in this case?
I believe that the basic premise of what you are trying to do is faulty if you are following what is considered to be proper use of inheritance to express subtyping relationships.
Inheritance can be used for various purposes and I am not wishing to pontificate upon the subject, but the opinion of most authorities is that inheritance is best and most safely used when used for subtyping. In short, an instance of a subclass should be able to be substituted for an instance of its base class without "breaking" the program (see: Liskov Substitution Principle).
Let us assume that OptimizedPoint is a subtype of Point. Then all the methods defined in class Point when invoked on an instance of OptimizedPoint will continue to function as expected. That means that OptimizedPoint cannot require any more stringent preconditions on any of these method invocations nor can it weaken any of the promissed postconditions that the contract Point has made.
But it is a common fallacy that just becuase OptimizedPoint is a subtype of Point that a container of OptimizedPoint, i.e. OptimizedTrack, is a subtype of a container of Point, i.e Track. This is because you cannot substitute an instance of OptimizedTrack for an instance of Track (due to your not being able to add an instance of Point to an instance of OptimizedTrack).
So, if you are trying to follow "good object-oriented design principles", it is disastrous trying to somehow make OptimizedTrack a subclass of Track, because it can certainly never be a subtype. You can, of course, reuse Track to build OptimizedTrack using composition, i.e. OptimizedTrack would contain within an instance of Track to which methods such as length would be delegated.
I'm not sure why you want to return a Track to your client code after the optimisation process, considering that OptimizedTrack is a Track itself. Below is a quick example of what I think you're trying to achieve (written in Kotlin because is less verbose).
You can achieve a lot more flexibility and solve the type issue if you consider Track to be an iterable object of points of type Point. This way, when you extend OptTrack from Track, you will be able to:
Substitute Track and OptTrack with no problem (even if your optimised track object has not computed a simplified Track object).
Simplify through optimize and return a Track from OptTrack with no issues (the optimize function on Point is irrelevant, you can return an OptPoint inside your Track because it extends the object Point)
open class Point(val x: Int, val y: Int, val z: Int) {
override fun toString(): String =
"Point(${this.x}, ${this.y}, ${this.z})"
}
data class OptPoint(val point: Point, val additional: Int):
Point(point.x, point.y, point.z) {
override fun toString(): String =
"OptPoint(${this.point}, ${this.additional})"
fun optimize(): Point {
return Point(this.x, this.y, this.z)
}
}
open class Track(private val points: Iterable<Point>): Iterable<Point> {
override operator fun iterator(): Iterator<Point> {
return this.points.iterator()
}
override fun toString(): String =
"Track(${this.points})"
}
data class OptTrack(private val points: Iterable<OptPoint>): Track(listOf()) {
override operator fun iterator(): Iterator<Point> {
return this.points.iterator()
}
fun optimize(): Track {
return Track(this.points.map{ it.optimize() })
}
}
fun main(args: Array<String>) {
val track: Track = OptTrack(listOf(
OptPoint(Point(1, 2, 3), 4))).optimize()
println(track)
// Track([Point(1, 2, 3)])
val other: Track = OptTrack(listOf(OptPoint(Point(1, 2, 3), 4)))
println(other)
// OptTrack(points=[OptPoint(Point(1, 2, 3), 4)])
}
In OOP you should prefer object composition to object inheritance. In your problem, I think creating interfaces for point and track could help. In order to achieve the proper result, I think, you should create two interfaces, IPoint & ITrack. Both Track and OptimizedTrack implement the ITrack interface and for common operations, you could create another class that both classes delegate the requests to it. After that you could create an strategy class, taking in an ITrack and returns another optimized ITrack. In the ITrack you could create GetPath which returns a list of objects of type IPoint.
I asked a question at How to design a complex class which incude some classes to make expansion easily in future in Kotlin? about how to design a complex class which incude some classes to make expansion easily in future in Kotlin.
A expert named s1m0nw1 give me a great answer as the following code.
But I don't know why he want to change MutableList to List at https://stackoverflow.com/posts/47960036/revisions , I can get the correct result when I use MutableList. Could you tell me?
The code
interface DeviceDef
data class BluetoothDef(val Status: Boolean = false) : DeviceDef
data class WiFiDef(val Name: String, val Status: Boolean = false) : DeviceDef
data class ScreenDef(val Name: String, val size: Long) : DeviceDef
class MDetail(val _id: Long, val devices: List<DeviceDef>) {
inline fun <reified T> getDevice(): T {
return devices.filterIsInstance(T::class.java).first()
}
}
Added
I think that mutableListOf<DeviceDef> is better than ListOf<DeviceDef> in order to extend in future.
I can use aMutableList.add() function to extend when I append new element of mutableListOf<DeviceDef>.
If I use ListOf<DeviceDef>, I have to construct it with listOf(mBluetoothDef1, mWiFiDef1, //mOther), it's not good. Right?
var aMutableList= mutableListOf<DeviceDef>()
var mBluetoothDef1= BluetoothDef(true)
var mWiFiDef1= WiFiHelper(this).getWiFiDefFromSystem()
aMutableList.add(mBluetoothDef1)
aMutableList.add(mWiFiDef1)
// aMutableList.add(mOther) //This is extension
var aMDetail1= MDetail(myID, aMutableList)
Sorry for not giving an explanation in the first place. The differences are explained in the docs.:
Unlike many languages, Kotlin distinguishes between mutable and immutable collections (lists, sets, maps, etc). Precise control over exactly when collections can be edited is useful for eliminating bugs, and for designing good APIs.
It is important to understand up front the difference between a read-only view of a mutable collection, and an actually immutable collection. Both are easy to create, but the type system doesn't express the difference, so keeping track of that (if it's relevant) is up to you.
The Kotlin List<out T> type is an interface that provides read-only operations like size, get and so on. Like in Java, it inherits from Collection<T> and that in turn inherits from Iterable<T>. Methods that change the list are added by the MutableList<T> interface. [...]
The List interface provides a read-only view so that you cannot e.g add new elements to the list which has many advantages for instance in multithreaded environments. There may be situations in which you will use MutableList instead.
I also recommend the following discussion:
Kotlin and Immutable Collections?
EDIT (added content):
You can do this is a one-liner without any add invocation:
val list = listOf(mBluetoothDef1, mWiFiDef1)
In Kotlin, a function with at least one argument can be defined either as a regular non-member function or as an extension function with one argument being a receiver.
As to the scoping, there seems to be no difference: both can be declared inside or outside classes and other functions, and both can or cannot have visibility modifiers equally.
Language reference seems not to recommend using regular functions or extension functions for different situations.
So, my question is: when do extension functions give advantage over regular non-member ones? And when regular ones over extensions?
foo.bar(baz, baq) vs bar(foo, baz, baq).
Is it just a hint of a function semantics (receiver is definitely in focus) or are there cases when using extensions functions makes code much cleaner or opens up opportunities?
Extension functions are useful in a few cases, and mandatory in others:
Idiomatic Cases:
When you want to enhance, extend or change an existing API. An extension function is the idiomatic way to change a class by adding new functionality. You can add extension functions and extension properties. See an example in the Jackson-Kotlin Module for adding methods to the ObjectMapper class simplifying the handling of TypeReference and generics.
Adding null safety to new or existing methods that cannot be called on a null. For example the extension function for String of String?.isNullOrBlank() allows you to use that function even on a null String without having to do your own null check first. The function itself does the check before calling internal functions. See documentation for extensions with Nullable Receiver
Mandatory Cases:
When you want an inline default function for an interface, you must use an extension function to add it to the interface because you cannot do so within the interface declaration (inlined functions must be final which is not currently allowed within an interface). This is useful when you need inline reified functions, for example this code from Injekt
When you want to add for (item in collection) { ... } support to a class that does not currently support that usage. You can add an iterator() extension method that follows the rules described in the for loops documentation -- even the returned iterator-like object can use extensions to satisfy the rules of providing next() and hasNext().
Adding operators to existing classes such as + and * (specialization of #1 but you can't do this in any other way, so is mandatory). See documentation for operator overloading
Optional Cases:
You want to control the scoping of when something is visible to a caller, so you extend the class only in the context in which you will allow the call to be visible. This is optional because you could just allow the extensions to be seen always. see answer in other SO question for scoping extension functions
You have an interface that you want to simplify the required implementation, while still allowing more easy helper functions for the user. You can optionally add default methods for the interface to help, or use extension functions to add the non-expected-to-be-implemented parts of the interface. One allows overriding of the defaults, the other does not (except for precedence of extensions vs. members).
When you want to relate functions to a category of functionality; extension functions use their receiver class as a place from which to find them. Their name space becomes the class (or classes) from which they can be triggered. Whereas top-level functions will be harder to find, and will fill up the global name space in IDE code completion dialogs. You can also fix existing library name space issues. For example, in Java 7 you have the Path class and it is difficult to find the Files.exist(path) method because it is name spaced oddly. The function could be placed directly on Path.exists() instead. (#kirill)
Precedence Rules:
When extending existing classes, keep the precedence rules in mind. They are described in KT-10806 as:
For each implicit receiver on current context we try members, then local extension functions(also parameters which have extension function type), then non-local extensions.
Extension functions play really well with the safe call operator ?.. If you expect that the argument of the function will sometimes be null, instead of early returning, make it the receiver of an extension function.
Ordinary function:
fun nullableSubstring(s: String?, from: Int, to: Int): String? {
if (s == null) {
return null
}
return s.substring(from, to)
}
Extension function:
fun String.extensionSubstring(from: Int, to: Int) = substring(from, to)
Call site:
fun main(args: Array<String>) {
val s: String? = null
val maybeSubstring = nullableSubstring(s, 0, 1)
val alsoMaybeSubstring = s?.extensionSubstring(0, 1)
As you can see, both do the same thing, however the extension function is shorter and on the call site, it's immediately clear that the result will be nullable.
There is at least one case where extension functions are a must - call chaining, also known as "fluent style":
foo.doX().doY().doZ()
Suppose you want to extend the Stream interface from Java 8 with you own operations. Of course, you can use ordinary functions for that, but it will look ugly as hell:
doZ(doY(doX(someStream())))
Clearly, you want to use extension functions for that.
Also, you cannot make ordinary functions infix, but you can do it with extension functions:
infix fun <A, B, C> ((A) -> B).`|`(f: (B) -> C): (A) -> C = { a -> f(this(a)) }
#Test
fun pipe() {
val mul2 = { x: Int -> x * 2 }
val add1 = { x: Int -> x + 1 }
assertEquals("7", (mul2 `|` add1 `|` Any::toString)(3))
}
There are cases where you have to use extension methods. E.g. if you have some list implementation MyList<T>, you can write an extension method like
fun Int MyList<Int>.sum() { ... }
It is impossible to write this as a "normal" method.