Basics
Basics 관련
공통
자주쓰는 Singleton Pattern
자주쓰는 Enum Pattern
자주쓰는 Documentation
Kotlin
자주쓰는 Builder Pattern
data class FooBar(
val a: String = ""
val b: String = ""
val c: Boolean = false,
val d: Boolean = false,
val e: String = ""
) {
class Builder {
private var bA: String = ""; fun a(block: () -> String) { bA = block() }
private var bB: String = ""; fun b(block: () -> String) { bB = block() }
private var bC: String = ""; fun c(block: () -> String) { bC = block() }
private var bD: String = ""; fun d(block: () -> String) { bD = block() }
private var bE: String = ""; fun e(block: () -> String) { bE = block() }
fun build(): FooBar = FooBar(bA, bB, bC, bD, bE)
}
companion object {
inline fun builder(block: Builder.() -> Unit): FooBar = Builder().apply(block).build()
}
}
in, out, and where
in, out, and where
They are variance modifiers and they help us allowing subtyping when using generics
Suppose we have a class called Case that will indicate if we either consume a weapon or produce it
class Case<T: Weapon> {
private val contents = mutableListOf<T>()
fun produce(): T = contents.last()
fun consume(item: T) = contents.add(item)
}
You might think that this is indeed possible thanks to polymorphism
val sniperRifle: Case<SniperRifle> = Case<SniperRifle>()
var rifle = Case<Rifle>()
rifle = sniperRifle // NOPE
Normally assigning a child instance to a parent instance is possible. But this time it isn't because generics doesn't allow subtyping by default and by that I mean we have to yous keywords like out and in to be able to use subtyping.
If we declare T with an out modifier, it will be convariant, so now we preserve subtyping but we cannot consume (take T as parameters) T, we can just produce (return) it.
class Case<out T: Weapon> {
private val contents = mutableListOf<T>()
fun produce(): T = contents.last()
// this is nolonger possible
// fun consume(item: T) = contents.add(item)
}
This is now possible
val sniperRifle: Case<SniperRifle> = Case<SniperRifle>()
var rifle = Case<Rifle>()
rifle = sniperRifle
rifle.produce()
if we declare T with an in modifier, it will be contravariant. Think of it as the other way around (sort of), because we can now consume T but we can't produce T
class Case<oin T: Weapon> {
private val contents = mutableListOf<T>()
// this is nolonger possible
// fun produce(): T = contents.last()
fun consume(item: T) = contents.add(item)
}
But here is where things get a little bit weird. Now a parent class will be the child of its child class, so rifle is now a subclass of sniperRifle so you treat it as if rifle is extending sniperRifle or as if weapon is extending knife and rifle
val sniperRifle: Case<SniperRifle> = Case<SniperRifle>()
var rifle = Case<Rifle>()
sniperRifle = rifle
sniperRifle.consume(Rifle())
Finally what is all about with the where keyword? We use it when we want to extend from several interfaces and not just one. This is also called an uppoer bound. Suppose we have a function to sell weapons and we want to sell just weapons and usable ones (note the new interface I made usable). If the data type we pass is a weapon and is usable, we can proceed to use the function, otherwise we can't. This is only possible with function
fun <T> sellWeapon(weapon: T): String where T : Weapon, T : Usable {
print("$weapon was just sold")
return "success"
}
Java 관련
Properties 파일객체 구성
파일:
src/main/resources/props/globals.properties
package com.example.markiiimark;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class FooBar {
private Properties config = null;
// ...[생략]...
public void loadProperties() {
try(InputStream io = Thread.currentThread().getContextClassLoader().getResourceAsStream("props/globals.properties")) {
this.config = new Properties();
this.config.load(io);
} catch(IOException e) {
// ...[생략]...
}
}
}