-
-
Save nathan130200/ea21d401eac03e671dcc23f1df537bb5 to your computer and use it in GitHub Desktop.
Basic Kotlin code example with very helpful comments.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package kunou.commands | |
import kunou.startup.Kunou | |
import net.dv8tion.jda.core.entities.Member | |
import net.dv8tion.jda.core.entities.Message | |
import org.slf4j.LoggerFactory | |
import java.lang.NumberFormatException | |
/* This is an example of primary constructors in Kotlin. Note the use of the "val" keyword, which declares | |
an immutable property/field/variable. Notice how it can be used in constructors to declare properties. | |
Question Marks on Types indicate that they're nullable. | |
?: is equivalent to: if this is null, evaluate some expression, otherwise return this and continue. | |
(Called an Elvis Operator). | |
Notice how on possibly-null things, to continue I have to do ?. which means: if null, return `null` with a `SomeType?` type, | |
otherwise, continue with the calls. | |
*/ | |
class CommandContext(val kunou: Kunou, val command: Command, val args: List<String>, val usedName: String, val msg: Message) { | |
val channel = msg.textChannel | |
val guild = msg.guild | |
val author = msg.author | |
val member = msg.member | |
val selfMember = guild.selfMember | |
/* This is an example of default values as well as tuples that can be destructured. | |
You have the Pair<A, B> class, the Triple<A, B, C> class, etc. | |
These are all "data classes" which can be destructured (discussed further below), which allows you | |
to break apart the contents into new variables on one line. | |
I could do this for example: | |
val (member, returnedMoreThanOneMember) = toMember() | |
// ^ Note the default value, I don't have to specify 0 again despite there not being overloads for that. | |
*/ | |
fun toMember(index: Int = 0): Pair<Member?, Boolean> { | |
if (index >= args.size || index < 0) | |
throw IndexOutOfBoundsException(index) | |
// This right here is an example of Kotlin replacing the .get method with the [] index operator. | |
val arg = args[index] | |
val mentionCheck = MENTION_REGEX.find(arg) | |
if (mentionCheck != null) { | |
val id: Long | |
try { | |
id = (mentionCheck.groups[1]?.value ?: return Pair(null, false)).toLong() | |
} catch (err: NumberFormatException) { | |
return Pair(null, false) | |
} | |
return Pair(guild.getMemberById(id), false) | |
} | |
val pairCheck = PAIR_REGEX.find(arg) | |
if (pairCheck != null) { | |
val username = pairCheck.groups[1]?.value ?: return Pair(null, false) | |
val discrim = pairCheck.groups[2]?.value | |
// Basically saying if discrim is not null, call let, which is basically if not null anyway. | |
// The use of "it" is discussed further below. | |
// Notice how the Consumers are replaced with the Kotlin Syntax. | |
// Yes you could replace this with an if not null statement. | |
discrim?.let { | |
return Pair(guild.members.firstOrNull { it.user.name.equals(username, true) && it.user.discriminator == discrim }, false) | |
} | |
val stream = guild.members.filter { it.user.name.equals(username, true) } | |
// Here is something you might not be used to: | |
return when (stream.count()) { | |
0 -> Pair(null, false) | |
1 -> Pair(stream.first(), false) | |
else -> Pair(null, true) | |
} | |
/* The if "statement" and the "when" statement are actually expressions in Kotlin, | |
meaning you can return them and assign them as values to variables. | |
This is also why Kotlin lacks a tertiary operator (a ? b : c) because | |
since "if" is an expression, you can just do: if (a) b else c | |
*/ | |
} | |
return Pair(null, false) | |
} | |
/* This is an example of default values as well as the Sequence API (not in Java, basically Streams) | |
and an extension function (joinToString). */ | |
fun concat(index: Int = 1): String { | |
if (index > args.size - 1 || index < 1) | |
throw IndexOutOfBoundsException(index) | |
return args.drop(index).joinToString(" ") | |
} | |
/* This is an example of default values as well as first-class functions. | |
(functions that can be stored and treated like normal variables without using stupid consumers, runnables, | |
etc. that waste a tincy bit of performance). | |
Basically means you don't have to create 3 different overloads for methods. You can also | |
refer back to previous function parameters. Kotlin also has super nice features such as named parameters | |
which allows you to do things literally impossible in Standard Java such as separate parameters after a varargs (Object... etc.) | |
parameter. | |
send("Hello there!") | |
send("Hello there!") { | |
// If the "last" parameter is a callback, you can put it outside the initial call. | |
// If this callback was only one line AND if we didn't already declare a lambda parameter here (which we have done) | |
// - you could refer to the Message object as "it". | |
// Theoretically you can use "it" on multiple lines but it's ambiguous so it's convention to declare a more | |
// - helpful parameter such as msg, which we do now just to show syntax. | |
// Notice no brackets/braces as you'd do in Java, as well as optional semi-colons. Even on multiple lines. | |
// Also notice the lack of getters/setters, as well as the println extension function. No more System.out :( | |
// Getters and Setters are replaced with properties which are so much better. | |
msg -> println(msg.contentRaw) | |
} | |
send("Hello there!", { | |
println(it.contentRaw) | |
}) { it.printStackTrace() } | |
// Notice how the new "last" parameter is placed outside the method call. | |
*/ | |
fun send(charSequence: CharSequence, successCallback: ((Message) -> Unit)? = null, | |
errorHandler: ((Throwable) -> Unit) = { command.error(this, it) }) { | |
try { | |
channel.sendMessage(charSequence).queue(successCallback, errorHandler) | |
} catch (err: Throwable) { | |
errorHandler(err) | |
} | |
} | |
/* This is destructuring, also an example of operator overloading. | |
Basically means you can do: | |
val (kunou, cmd, args) = context | |
or | |
val (_, _, args) = context | |
*/ | |
operator fun component1(): Kunou = kunou | |
operator fun component2(): Command = command | |
operator fun component3(): List<String> = args | |
// Pretty much the equivalent to the Java Pattern/Matcher classes. | |
companion object { | |
private val MENTION_REGEX = Regex("^(?:<@!?)?(\\d+)(?:>)$") | |
private val PAIR_REGEX = Regex("^([.[^#]]{2,32})(?:#(\\d{4}))?$") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment