Skip to main content

Basics

About 3 minJavaBasicsjavajdkjdk7jdk8singletonenumjavadocskotlinkdoccompanion-objectlomboklog4jlog4j2

Basics 관련


공통

자주쓰는 Singleton Pattern

Java
public final class FooBar {
    private static final FooBar INSTANCE = null;
    private FooBar() {}
    public static synchronized FooBar getInstance() {
        if (INSTANCE == null)
            INSTANCE = new FooBar();
        return INSTANCE;
    }
    // ...[생략]...
}

자주쓰는 Enum Pattern

Java

Lombok을 사용하여 코드를 간결하게 작성

package com.example.markiiimark;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Getter
@AllArgsConstructor
public enum FooBar {
    A("a", "1", 1),
    B("b", "2", 2),
    C("c", "3", 3);
    private String code;
    private String a;
    private int b;
    
    private static Map<String, FooBar> findMap 
        = new ConcurrentHashMap<>();
    static {
        for (Foobar item: values())
            findMap.put(item.getCode(), item);
    }
    public static Foobar findByCode(int i) { return findMap.get(i); }

    // ...[생략]... 이 부분 부터는 알아서 응용 코드 적용
}

자주쓰는 Documentation

Java

for Javadoc

package com.example.markiiimark;

/**
 * FooGaze
 * 
 * 클래스는 무엇 무엇을 합니다.
 * @since 2022.12.12
 * @author chlee
 * @see com.example.marikiimark.FooBar
 */
public class FooGaze {

    /** 
     * doStuff
     * 뭘 할 것인가
     * 
     * @param input {@link String} 입력값
     * @returns {@link String} 출력값
     */
    public String doStuff(String input) {
        //...[생략]...
        return input + " a";
    }
}

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) {
            // ...[생략]...
        }
    }
}

이찬희 (MarkiiimarK)
Never Stop Learning.