Kotlin multiline-string annotation parameters - kotlin

In Java, now that it supports text blocks, you can do this:
#Schema(description = """
Line one.
Line two.
""")
public void someMethodName() { ... }
In Java, text blocks are compile-time constants and they automatically remove the indents. But in Kotlin, if you do this:
#Schema(description = """
Line one.
Line two.
""")
fun someMethodName() { ... }
you end up with unwanted spaces in front of each line. Unfortunately, you can't use trimMargin() or trimIndent() because they are not compile-time constants. Is there a way to make it look as nice in Kotlin as it does in Java?

Unfortunately for your use case, I don't think so. The point of the triple quote is to provide a way to write "Formatted" text into a string. If Java doesn't behave the same way as Kotlin, then technically it's the odd one out as any other language I've used behaves the same way as Kotlin. Your best alternative would be something like the following:
#Schema(description = "Line one.\n"
+ "Line two.\n"
+ "Line three.")
fun someMethodName() { ... }
The string concatenation will be performed at compile time since it is between literals.

Related

How do I specify an ActionListener in Kotlin?

I want to add an ActionListener to a JButton in Kotlin. In Java, I would just write this:
JPanel makeButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
JButton dirButton = new JButton("Change directory");
dirButton.addActionListener(e -> chooseDirectory());
panel.add(dirButton)
return panel;
}
But it's not so simple in Kotlin. I first tried this:
private fun makeButtonPanel() : JPanel {
val panel = JPanel(FlowLayout())
val dirButton = JButton("Choose")
dirButton.addActionListener(e -> chooseDirectory()) // error message here
// ...
}
private fun chooseDirectory() { ... }
But I'm getting this error message:
Type Mismatch
Required: ((ActionEvent!) -> Unit)!
Found: KFunction1<ActionEvent, Unit>
I understand that the ! means that this is a java method with uncertain nullability, but that doesn't help me understand how to write it. All I want it to do is call the chooseDirectory() method. There must be a clean, simple way to do this, but I don't see it.
As you've discovered, you need to use braces ({ }).
This is because braces are a necessary part of defining a lambda in Kotlin.  (That differs from languages like Java and Scala, where the necessary part is the -> or => arrow.  That's because in Kotlin the arrow is optional if there are one or no parameters; if one, the it keyword is used.)
Without the braces, the code would call your chooseDirectory() function, and try to pass its result to addActionListener() — which obviously wouldn't work.
Braces are also sufficient: they're taken as defining a lambda unless you're giving the body of a function or method or an if/when branch.  (Again, this differs from most C/Java-like languages.  In Kotlin, if you just want a block scope, you have to use a construct such as run.)
As for the parentheses, they're optional here.  You could include them if you wanted:
dirButton.addActionListener({ chooseDirectory() })
But Kotlin has a convention that if a function's last parameter is a function, you can pass it after the parens:
dirButton.addActionListener(){ chooseDirectory() }
And if that would make the parens empty, then you can omit them entirely:
dirButton.addActionListener{ chooseDirectory() }
That's to allow functions that look like new language syntax.  For example, you may have met the with function:
with(someObject) {
itsProperty = someValue
}
That's just a perfectly ordinary function, defined in the standard library, and taking a function as its last parameter.  Similarly, repeat:
repeat(10) {
// Some code to be run 10 times…
}
There's one further thing worth mentioning here.  In Kotlin, lambdas are one way to define functions, which are first-class types and can be defined, passed around, and used just like other types.  This differs from Java, which has traditionally used interfaces for those purposes — often interfaces with a Single Abstract Method (‘SAM interfaces’) — and in which lambdas are little more than syntactic sugar for defining an anonymous implementation of such an interface.
As a special case, for interoperability, Kotlin allows a lambda to define an implementation of a Java SAM interface (or, since Kotlin 1.4, of a Kotlin fun interface), instead of a function.
ActionListener is a Java SAM interface, which is why you can use a lambda here.
Okay, I figured it out, and it was pretty simple. I just have to dispense with the parentheses and say
dirButton.addActionListener { chooseDirectory() }
I'm still not clear on when I should use braces instead of parentheses.

Kotlin string concatenation - preserving indents with multiline strings

Kotlin's multiline string handling is wonderful in so many ways, and it's ability to .trimIndent() allows you to keep a string literal indented with the rest of the code like so:
fun main(args: Array<String>) {
val myString = """
hello
world
""".trimIndent()
println(myString)
Will print out:
hello
world
without the literal indents present in the code. But this breaks down when using Kotlin's powerful string templating system if the value being inserted is multiline:
fun main(args: Array<String>) {
val myString = """
hello
world
""".trimIndent()
println("""
teststring2
$myString
""".trimIndent())
}
will print out
teststring2
hello
world
This appears to happen because the world is on a new line and doesn't receive the indentation that hello gets. Is there an elegant way to handle this?
Here's the first string literal.  (To avoid confusion, I'll use underscores to represent spaces.)
____hello
____world
Its trimIndent() call will remove the spaces, leaving it with no indentation, like this:
hello
world
The second string looks like this:
________teststring2
________$myString
Then the $myString will be substituted precisely with the contents of the first:
________teststring2
________hello
world
Note that the indentation that was before $myString remains, giving the spaces you see before the "hello"; but there's no indentation within myString, so you don't see any before "world".
Now, trimIndent() removes “a common minimal indent of all the input lines”.  But there's no common indent here, because of the last line.  So the second trimIndent() call does nothing.
As to what to do, there are probably several options.  If you don't want any indents, then the most obvious would be to remove all indents, not just common ones.  (I don't think there's a dedicated function in the standard library, but it should be easy enough to write one.  For example, you could do a regular expression replace of "^ *" with "".)
This is a common problem in practice, not trimIndent()'s problem. #gidds has explained it very clearly. If you want to achieve what you want, you can use trimMargin().
fun main(args: Array<String>) {
val myString = """
hello
world
""".trimIndent()
println("""
|teststring2
|$myString
""".trimMargin())
}
Output:
teststring2
hello
world

What's the advantage of creating a one line function that contains a string, that wouldn't be achievable by just assigning a string to a variable?

In Kotlin, I understand that a string can be assigned to a function directly, such as:
fun foo(): String = "Hello World"
But you can also assign a String to a variable directly as well:
var foobar: String = "Hello Word"
My question is, why would you ever create a function when you could just create a variable? I can't see the point in the existence of this functionality.
The interesting thing about
fun foo(): String = "Hello World"
is that it is using the expression syntax, and is equivalent to:
fun foo(): String {
return "Hello World"
}
While just returning a constant isn't very useful, using the expression syntax, avoiding the {block} and return statements allows much more concise function definitions in the case where the entire function can be expressed in one expression.
For example, if foo() was a method on a class, you could say hello with a property of that class:
class Hello(var what : String = "World") {
fun foo(): String = "Hello, $what!"
}
fun main() {
val hello = Hello()
println(hello.foo())
hello.what = "Universe"
println(hello.foo())
}
This prints:
Hello, World!
Hello, Universe!
This is more about when to prefer a function v/s a property.
Kotlin coding conventions has a section that describes this.
A property should be preferred over a function when-
the underlying algorithm does not throw
value is cheap to calculate (or caсhed on the first run)
returns the same result over invocations if the object state hasn't changed
In terms of an API use-case, in some cases exposing a function instead of a property might be a good idea as that gives you the scope to change the implementation of this API in future. What might be a hardcoded value today, could be replaced by code that computes the value in future.
It's simple, think about what the word coding means. Coding rules. Rules that are complicated get broken down in rules that are exactly one level below in abstraction, so that the program is as intelligible as possible.
Well, the function name is just one level above the expression. This is even more true in a language as expressive as kotlin where one line can easily be equivalent to several lines of Java.
If you are talking about strings or primitives exclusively then, yes, an attribute is a more natural choice than a function.

What do grave accents do/mean in Kotlin?

In as many expressions/definitions as possible please.
I'm writing a test function, where after the call fails, the function returns:
`this `fails with` "the state is propagated"`
(with the grave accents surrounding fails with ^ i don't know how to escape, sorry)
You want to use them when something is a Kotlin keyword (like Java's System.in) but you need to call it. Then you can do
System.`in`
instead to make it work.
You can also use this in variables, functions, classes and any other identifiers. There is a small paragraph on this topic on Kotlin's documentation.
Actually, it is more than that.
You can use any class, function, variable, or identifier whose name contains spaces or symbols with grave accents.
class `Class name with spaces` {
fun `method name with spaces, +, -`(`a parameter`: Int) {
val `variable?!` = `a parameter` + 1
println(`variable?!`.toString())
}
}
fun main(args: Array<String>) {
val instance = `Class name with spaces`()
instance.`method name with spaces, +, -`(100)
}
This is a compilable and working code:
This is often used in testing, in order to make the test method names self-explanatory.
class OperationsUnitTest {
#Test
fun `addition should be commutative`() {
...
}
}

final or val function parameter or in Kotlin?

Why does Kotlin removed the final or val function parameter which is very useful in Java?
fun say(val msg: String = "Hello World") {
msg = "Hello To Me" // would give an error here since msg is val
//or final
...
...
...
}
Kotlin function parameters are final. There is no val or final keyword because that's the default (and can't be changed).
After Kotlin M5.1 support of mutable parameters removed, In earlier versions that can be achieve using
fun foo(var x: Int) {
x = 5
}
According to Kotlin developers, main reasons of removing this feature are below -
The main reason is that this was confusing: people tend to think that this means passing a parameter by reference, which we do not support (it is costly at runtime).
Another source of confusion is primary constructors: “val” or “var” in a constructor declaration means something different from the same thing if a function declarations (namely, it creates a property).
Also, we all know that mutating parameters is no good style, so writing “val” or “var” infront of a parameter in a function, catch block of for-loop is no longer allowed.
Summary - All parameter values are val now. You have to introduce separate variable for re-initialising. Example -
fun say(val msg: String) {
var tempMsg = msg
if(yourConditionSatisfy) {
tempMsg = "Hello To Me"
}
}
And another reason is that val and var differ by only one letter. This can be very confusing. So for function parameters they removed the option completely. Thus eliminating the confusion in this one small area (yet keeping it everywhere else--go figure).
This decision was made to avoid fragile base class problem. It happens when a small change in base classes (superclasses) makes subclasses malfunction.