F-lab Java 1์ฃผ์ฐจ / Phase 2 / Unit 2.2 ๋ณธ๊ฒฉ ํ์ต ์๋ฃ
9-์น์ ๋ง์คํฐ ํ๋กฌํํธ ํ์์ผ๋ก ๊น์ด ํํค์น๋ค.์ ์ ์ง์: Unit 2.1 (๋ฉ์๋์ ๊ตฌ์กฐ)
๋ค์ Unit: 2.3 โ ์์๊ณผ ์์ฑ์ ์ฒด์ด๋
๋งํธ ๊ณ์ฐ๋์์ ๊ฒฐ์ ํ ๋๋ฅผ ์๊ฐํด๋ณด์ธ์. ์๋๋ง๋ค ์ฌ๋ ๋ฌผ๊ฑด์ ๊ฐ์๊ฐ ๋ค๋ฆ ๋๋ค:
๊ณ์ฐ์์ด ์ผํ๋ ๋ฐฉ์:
๋ง์ฝ ๊ณ์ฐ์์ด ์ด๋ฐ ์์ด๋ผ๋ฉด ๋์ฐํ ๊ฒ:
โ ๊ฐ๋ณ์ธ์๋ "๊ฐ์์ ์๊ด์์ด ๋ฐ์์ฃผ๋ ๊ณ์ฐ๋" ์ ์ ์ .
System.out.printf์๋ฐ๋ฅผ ์จ๋ณธ ์ฌ๋์ด๋ผ๋ฉด ์ด๋ฏธ ๊ฐ๋ณ์ธ์๋ฅผ ๋งค์ผ ์ฐ๊ณ ์์ต๋๋ค:
System.out.printf("์ด๋ฆ: %s%n", "Alice");
System.out.printf("์ด๋ฆ: %s, ๋์ด: %d%n", "Alice", 25);
System.out.printf("์ขํ: (%d, %d, %d)%n", 1, 2, 3);
๊ฐ์ printf ๋ฉ์๋์ธ๋ฐ ์ธ์ ๊ฐ์๊ฐ ๋ค ๋ค๋ฆ. ์ด๋ป๊ฒ ๊ฐ๋ฅํ ๊น์?
printf ์ ์๊ทธ๋์ฒ:
public PrintStream printf(String format, Object... args) { ... }
// โ ๊ฐ๋ณ์ธ์
โ Object... args ๊ฐ "๋ช ๊ฐ๋ ๋ฐ์์ฃผ๊ฒ ๋ค" ๋ ์ ์ธ.
์ด๊ฒ ๊ฐ๋ณ์ธ์(varargs).
| ๋น์ ์์ | ์๋ฐ ๊ฐ๋ณ์ธ์ |
|---|---|
| ๋งํธ ๊ณ์ฐ๋ | ๊ฐ๋ณ์ธ์ ๋ฉ์๋ |
| ์นดํธ์ ๋ฌผ๊ฑด๋ค | ๊ฐ๋ณ์ธ์๋ก ์ ๋ฌ๋๋ ์ธ์๋ค |
| 1๊ฐ๋ 100๊ฐ๋ OK | 0๊ฐ๋ถํฐ N๊ฐ๊น์ง ์์ |
| ํ ์ค์ ๋ชจ๋ ์๋ ์ฒ๋ฆฌ | ํ ๋ฉ์๋๋ก ๋ชจ๋ ์๋๋ฆฌ์ค ์ฒ๋ฆฌ |
์๋ฐ 1.4 ์ด์ (2004๋ ์ด์ ), ๊ฐ๋ณ์ธ์๊ฐ ์์์ต๋๋ค. ์ธ์ ๊ฐ์๊ฐ ๋ค์ํ ๋ฉ์๋๋ฅผ ๋ง๋ค๋ ค๋ฉด?
๋ฐฉ๋ฒ 1 โ ์ค๋ฒ๋ก๋ฉ ํญ์ฆ:
public class Logger {
public void log(String msg1) {
System.out.println(msg1);
}
public void log(String msg1, String msg2) {
System.out.println(msg1 + " " + msg2);
}
public void log(String msg1, String msg2, String msg3) {
System.out.println(msg1 + " " + msg2 + " " + msg3);
}
public void log(String msg1, String msg2, String msg3, String msg4) {
System.out.println(...);
}
// ... 5๊ฐ, 6๊ฐ, 7๊ฐ... ๋์์ด โ
}
๋ฌธ์ :
๋ฐฉ๋ฒ 2 โ ๋ฐฐ์ด๋ก ๋ฐ๊ธฐ:
public class Logger {
public void log(String[] messages) {
for (String msg : messages) {
System.out.println(msg);
}
}
}
// ์ฌ์ฉ โ ๋งค๋ฒ ๋ฐฐ์ด์ ๋ง๋ค์ด์ผ ํจ โ
logger.log(new String[]{"hello"});
logger.log(new String[]{"hello", "world"});
logger.log(new String[]{"a", "b", "c", "d"});
๋ฌธ์ :
new String[]{...} ์์ฑ โ ๋ฒ๊ฑฐ๋ก์Java 5 ์์ ... ๋ฌธ๋ฒ ์ด ๋ฑ์ฅ:
public class Logger {
public void log(String... messages) { // โ ๊ฐ๋ณ์ธ์
for (String msg : messages) {
System.out.println(msg);
}
}
}
// ์ฌ์ฉ โ ์์ฐ์ค๋ฌ์ โ
logger.log("hello");
logger.log("hello", "world");
logger.log("a", "b", "c", "d");
logger.log(); // 0๊ฐ๋ OK!
ํจ๊ณผ:
โ Java 5๋ ๊ฐ๋ณ์ธ์ ์ธ์๋ ์ ๋ค๋ฆญ, ์ด๋ ธํ ์ด์ , enum ๋ฑ ์๋ฐ ์ญ์ฌ์ ๋ถ์๋ น.
"๊ฐ๋ณ์ธ์๋ '๋ฉ์๋์ ์ ๋ ฅ ์ ์ฐ์ฑ' ์ ๊ทน๋ํํ๊ธฐ ์ํ ๋ฌธ๋ฒ์ ์คํ(syntactic sugar)์ด๋ค."
๋ด๋ถ์ ์ผ๋ก๋ ์ฌ์ ํ ๋ฐฐ์ด ์ ๋๋ค. ๋จ์ง ํธ์ถํ๋ ์ฌ์ฉ์๊ฐ ํธํ๊ฒ ์ฐ๋๋ก ํด์ค ๊ฒ๋ฟ.
๊ฐ๋ณ์ธ์๊ฐ ์์ ๋ ์ด๋ค ๋ถํธํจ์ด ์๋์ง ์ค์ ์๋๋ฆฌ์ค ๋ก ๋ณด๊ฒ ์ต๋๋ค.
์ด์ ๊ฒฌ์ ๊ฒ์์์ ๋ค์ํ ์กฐ๊ฑด์ ๋ฐ๊ณ ์ถ๋ค๊ณ ํฉ์๋ค:
public class FareSearchService {
public List<Fare> search(int minPrice) { ... }
public List<Fare> search(int minPrice, int maxPrice) { ... }
public List<Fare> search(int minPrice, int maxPrice, String currency) { ... }
public List<Fare> search(int minPrice, int maxPrice, String currency, String origin) { ... }
public List<Fare> search(int minPrice, int maxPrice, String currency, String origin, String dest) { ... }
// ... ์๋๋ฆฌ์ค ์ถ๊ฐ๋ ๋๋ง๋ค ๋ฉ์๋ ์ถ๊ฐ โ
public List<Fare> search(int maxPrice, boolean isMaxOnly) {
// "์ต๋ ๊ฐ๊ฒฉ๋ง์ผ๋ก ๊ฒ์" ์๋๋ฆฌ์ค โ ๋งค๊ฐ๋ณ์๊ฐ ๊ฐ์ ํ์
์ด๋ผ ๋ณ๋ ์ฒ๋ฆฌ โ
}
}
๋ฌธ์ :
1. ๋ฉ์๋ ํญ์ฆ โ ์กฐํฉ๋ง๋ค ์ ๋ฉ์๋
2. ํ์
์ด ๊ฐ์ผ๋ฉด ๋ชจํธํจ (์ ์ฝ๋์ ๋ง์ง๋ง ๋ฉ์๋ ์ฐธ๊ณ )
3. ์ ์ง๋ณด์ ์ง์ฅ โ ๊ฒ์ ๋ก์ง ๋ณ๊ฒฝ ์ ๋ชจ๋ ๋ฉ์๋ ์์
public class FareSearchService {
public List<Fare> search(SearchCriteria[] criteria) { ... }
}
// ์ฌ์ฉ โ ๋ฐฐ์ด ๋งค๋ฒ ์์ฑ โ
service.search(new SearchCriteria[]{
new SearchCriteria("price", ">=", 1000)
});
service.search(new SearchCriteria[]{
new SearchCriteria("price", ">=", 1000),
new SearchCriteria("price", "<=", 5000)
});
// "์กฐ๊ฑด ์์ด ์ ์ฒด ๊ฒ์"
service.search(new SearchCriteria[0]); // ๋น ๋ฐฐ์ด โ ์ด์ํจ
๋ฌธ์ :
1. ํธ์ถ ์ ๋
ธ์ด์ฆ โ new SearchCriteria[]{...} ๋งค๋ฒ
2. ๋น ์ธ์ ํํ ์ด์ โ new SearchCriteria[0]
3. ๊ฐ๋
์ฑ โ
public class FareSearchService {
public List<Fare> search(SearchCriteria... criteria) { ... }
}
// ์ฌ์ฉ โ ์์ฐ์ค๋ฌ์ โ
service.search(); // ์กฐ๊ฑด ์์ โ ์ ์ฒด ๊ฒ์
service.search(priceMin);
service.search(priceMin, priceMax);
service.search(priceMin, priceMax, currency, origin, dest);
ํจ๊ณผ:
๊ฐ๋ณ์ธ์๊ฐ ์์ผ๋ฉด ์๋ฐ ์์ฒด๊ฐ ๋งค์ฐ ๋ถํธํ ๊ฒ์ ๋๋ค:
// 1. printf ๊ณ์ด
System.out.printf("์ด๋ฆ: %s, ๋์ด: %d", name, age);
// 2. String.format
String.format("Hello, %s!", "Alice");
// 3. List.of, Set.of, Map.of (Java 9+)
List.of("a", "b", "c");
Set.of(1, 2, 3, 4, 5);
// 4. Arrays.asList
Arrays.asList("x", "y", "z");
// 5. String.join
String.join(", ", "a", "b", "c"); // "a, b, c"
// 6. Stream.of
Stream.of(1, 2, 3, 4, 5);
โ ๋ชจ๋ ๊ฐ๋ณ์ธ์ ๋๋ถ. ์์๋ค๋ฉด ์ด ๋ชจ๋ ๊ฒ ๋ฐฐ์ด์ ๋งค๋ฒ ๋ง๋ค์ด์ผ ํ์ ๊ฒ.
[์ ๊ทผ์ ์ด์] ๋ฐํํ์
๋ฉ์๋๋ช
(ํ์
... ๋ณ์๋ช
) {
// ๋ณ์๋ช
์ ๋ฐฐ์ด์ฒ๋ผ ๋ค๋ฃธ
}
ํต์ฌ ํ์: ... (์ 3๊ฐ)
public class Logger {
public void log(String... messages) {
// messages๋ String[] ๋ฐฐ์ด๋ก ๋ค๋ฃธ
System.out.println("๋ฐ์ ๋ฉ์์ง ๊ฐ์: " + messages.length);
for (String msg : messages) {
System.out.println("- " + msg);
}
}
}
Logger logger = new Logger();
logger.log(); // 0๊ฐ
logger.log("hello"); // 1๊ฐ
logger.log("hello", "world"); // 2๊ฐ
logger.log("a", "b", "c", "d", "e"); // 5๊ฐ
์ถ๋ ฅ:
๋ฐ์ ๋ฉ์์ง ๊ฐ์: 0
๋ฐ์ ๋ฉ์์ง ๊ฐ์: 1
- hello
๋ฐ์ ๋ฉ์์ง ๊ฐ์: 2
- hello
- world
๋ฐ์ ๋ฉ์์ง ๊ฐ์: 5
- a
- b
- c
- d
- e
public void process(int... numbers) {
// numbers๋ int[]
// 1. length ์ฌ์ฉ ๊ฐ๋ฅ
System.out.println("๊ฐ์: " + numbers.length);
// 2. ์ธ๋ฑ์ค ์ ๊ทผ ๊ฐ๋ฅ
if (numbers.length > 0) {
System.out.println("์ฒซ ๋ฒ์งธ: " + numbers[0]);
}
// 3. for-each ๊ฐ๋ฅ
for (int n : numbers) {
System.out.println(n);
}
// 4. for ๋ฃจํ ๊ฐ๋ฅ
for (int i = 0; i < numbers.length; i++) {
System.out.println(i + ": " + numbers[i]);
}
// 5. Arrays ์ ํธ ์ฌ์ฉ ๊ฐ๋ฅ
int sum = Arrays.stream(numbers).sum();
}
โ ํธ์ถํ๋ ์ชฝ์ ์ธ์ ๋์ด, ๋ฐ๋ ์ชฝ์ ๋ฐฐ์ด ์ฒ๋ฆฌ.
๊ฐ๋ณ์ธ์๋ ๋ค๋ฅธ ๋งค๊ฐ๋ณ์์ ํจ๊ป ์ฌ์ฉ ๊ฐ๋ฅ:
public class Logger {
public void log(String level, String... messages) {
for (String msg : messages) {
System.out.println("[" + level + "] " + msg);
}
}
}
logger.log("INFO"); // 0๊ฐ ๋ฉ์์ง
logger.log("INFO", "์์"); // 1๊ฐ
logger.log("ERROR", "DB ์ฐ๊ฒฐ ์คํจ", "์ฌ์๋ ์ค"); // 2๊ฐ
logger.log("DEBUG", "a", "b", "c"); // 3๊ฐ
public void log(String level, String... messages) { ... } // โ
๋ง์ง๋ง
public void log(String... messages, String level) { ... } // โ ์ปดํ์ผ ์๋ฌ
์?: ์๋ฐ๊ฐ ์ด๋๊น์ง๊ฐ ๊ฐ๋ณ์ธ์์ธ์ง ๊ตฌ๋ณ ๋ถ๊ฐ.
logger.log("INFO", "msg1", "msg2", "ERROR");
// "INFO"๊ฐ ์ฒซ ์ธ์? "ERROR"๊ฐ level? ์๋ฐ๊ฐ ๋ชจ๋ฆ
public void method(String... s, int... i) { ... } // โ ์ปดํ์ผ ์๋ฌ
์ ๊ท์น 1๊ณผ ๊ฐ์ ์ด์ โ ์ด๋๊น์ง๊ฐ ์ฒซ ๊ฐ๋ณ์ธ์, ์ด๋๋ถํฐ ๋ ๋ฒ์งธ์ธ์ง ๋ชจ๋ฆ.
public void log(String... messages) { ... }
logger.log(); // 0๊ฐ OK
logger.log("a"); // 1๊ฐ OK
logger.log("a", "b"); // 2๊ฐ OK
// 100๊ฐ๋ OK
"๊ฐ๋ณ์ธ์๋ ์ปดํ์ผ๋ฌ๊ฐ ์๋์ผ๋ก ๋ฐฐ์ด๋ก ๋ณํํด์ฃผ๋ ๋ฌธ๋ฒ์ ์คํ(syntactic sugar)"
์ค์ ๋์:
// ์ฐ๋ฆฌ๊ฐ ์์ฑํ ์ฝ๋
public void log(String... messages) {
System.out.println(messages.length);
}
logger.log("a", "b", "c");
์ปดํ์ผ๋ฌ๊ฐ ๋ณํํ ์ฝ๋:
public void log(String[] messages) { // โ ์ฌ์ค์ ๋ฐฐ์ด
System.out.println(messages.length);
}
logger.log(new String[]{"a", "b", "c"}); // โ ํธ์ถ ์ ์๋์ผ๋ก ๋ฐฐ์ด ์์ฑ
โ JVM ์ ์ฅ์์๋ ๊ฐ๋ณ์ธ์์ ๋ฐฐ์ด์ด ์์ ํ ๋์ผ.
logger.log("a", "b", "c");
JVM ๋ด๋ถ ํ๋ฆ:
1. ํธ์ถ ์์ :
- JVM์ด "a", "b", "c"๋ฅผ ๋ด์ String[] ๋ฐฐ์ด์ Heap์ ์์ฑ
- ๊ทธ ๋ฐฐ์ด์ ์ฐธ์กฐ๋ฅผ log ๋ฉ์๋์ messages ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ
2. ๋ฉ์๋ ์์์:
- messages๋ ๊ทธ ๋ฐฐ์ด์ ๊ฐ๋ฆฌํค๋ ์ฐธ์กฐ
- ๋ฐฐ์ด์ ๋ชจ๋ ๋ฉ์๋/์์ฑ ์ฌ์ฉ ๊ฐ๋ฅ
3. ๋ฉ์๋ ์ข
๋ฃ ํ:
- ๋ฐฐ์ด์ ๋ ์ด์ ์ฐธ์กฐ๋์ง ์์
- ๋ค์ GC์์ ์๊ฑฐ๋จ
โ ๊ฐ๋ณ์ธ์ ํธ์ถ์ ๋งค๋ฒ ์ ๋ฐฐ์ด ์์ฑ (์ฑ๋ฅ ์ํฅ ๊ฐ๋ฅ โ 7๋ฒ ์น์ ์์ ์์ธํ)
public void log(String... messages) { ... }
// 1. ์ผ๋ฐ์ ์ธ ํธ์ถ โ ์ธ์ ๋์ด
logger.log("a", "b", "c");
// 2. ๋ฐฐ์ด์ ์ง์ ์ ๋ฌ
String[] arr = {"a", "b", "c"};
logger.log(arr); // โ
๊ฐ๋ฅ
// 3. new๋ก ๋ง๋ ๋ฐฐ์ด ์ง์ ์ ๋ฌ
logger.log(new String[]{"a", "b", "c"}); // โ
๊ฐ๋ฅ (๊ฐ๋ณ์ธ์ ์์ ๋ ๋ฐฉ์)
์?: ์ด์ฐจํผ ๋ด๋ถ์ ์ผ๋ก ๋ฐฐ์ด์ด๋ผ์.
null ์ ๋ฌ ์ ํจ์ โ ๏ธpublic void log(String... messages) {
System.out.println(messages.length); // โ ๏ธ
}
// 1. null ์ง์ ์ ๋ฌ
logger.log(null); // โ messages๊ฐ null
// โ messages.length โ NullPointerException!
// 2. null ์ธ์
logger.log("a", null, "b"); // โ messages = ["a", null, "b"]
// โ messages.length = 3 (OK)
// โ messages[1] ์ฌ์ฉ ์ NullPointerException ๊ฐ๋ฅ
// 3. ๋น ๋ฐฐ์ด๊ณผ ๋ค๋ฆ
logger.log(new String[0]); // โ messages = [] (๋น ๋ฐฐ์ด)
// โ messages.length = 0
์์ ํ ์ฝ๋:
public void log(String... messages) {
if (messages == null) {
return; // ๋๋ ๋น ๋ฐฐ์ด๋ก ์ฒ๋ฆฌ
}
for (String msg : messages) {
if (msg != null) {
System.out.println(msg);
}
}
}
public void process(Integer... nums) { ... }
process(1, 2, 3); // int โ Integer ์๋ ๋ฐ์ฑ
// โ new Integer[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)}
// โ 3๋ฒ์ ๋ฐ์ฑ ๋ฐ์ (์ฑ๋ฅ ์ํฅ)
๋์ โ ๊ธฐ๋ณธํ ๊ฐ๋ณ์ธ์:
public void process(int... nums) { ... }
process(1, 2, 3); // โ new int[]{1, 2, 3}
// โ ๋ฐ์ฑ ์์ โ
โ ๊ธฐ๋ณธํ์ด ๊ฐ๋ฅํ ์๋๋ฆฌ์ค๋ ๊ธฐ๋ณธํ ๊ฐ๋ณ์ธ์ ๊ถ์ฅ.
ILIC ์ด์ ์์คํ ์์ ๊ฐ๋ณ์ธ์๋ฅผ ๋ค์ํ๊ฒ ํ์ฉํ๋ ์์๋ค.
public class IlicLogger {
public void info(String message, Object... args) {
String formatted = String.format(message, args);
System.out.println("[INFO] " + formatted);
}
public void error(String message, Throwable error, Object... args) {
String formatted = String.format(message, args);
System.err.println("[ERROR] " + formatted);
error.printStackTrace();
}
}
// ์ฌ์ฉ
logger.info("์ด์ ์์ฑ ์๋ฃ");
logger.info("์ด์ ID: %d", 100);
logger.info("๊ณ ๊ฐ %s, ๊ธ์ก %d์, ํตํ %s", "Alice", 50000, "KRW");
logger.error("DB ์ฐ๊ฒฐ ์คํจ", exception);
logger.error("์ด์ %d ์ฒ๋ฆฌ ์คํจ: %s", exception, fareId, reason);
โ SLF4J, Log4j ๋ชจ๋ ์ด๋ฐ ํจํด ์ฌ์ฉ.
public class FareValidator {
public static void validateNotNull(String fieldName, Object... values) {
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
throw new IllegalArgumentException(
fieldName + "[" + i + "] ๋ null์ผ ์ ์์ต๋๋ค"
);
}
}
}
public static int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
public static int max(int first, int... rest) {
// ์ต์ 1๊ฐ๋ ํ์, ๋๋จธ์ง๋ ์ ํ
int max = first;
for (int n : rest) {
if (n > max) max = n;
}
return max;
}
}
// ์ฌ์ฉ
FareValidator.validateNotNull("๊ณ ๊ฐ ์ ๋ณด", customerId, customerName, email);
int total = FareValidator.sum(1000, 2000, 3000); // 6000
int largest = FareValidator.max(10, 5, 8, 12, 3); // 12
FareValidator.max(10); // 10 (rest๋ ๋น ๋ฐฐ์ด)
max(int first, int... rest) ํจํด โญ :
public class FareQuery {
private final List<String> conditions = new ArrayList<>();
public FareQuery in(String field, Object... values) {
if (values.length == 0) {
return this; // ๋ฌด์
}
String inClause = field + " IN (" +
Arrays.stream(values).map(v -> "?").collect(Collectors.joining(",")) +
")";
conditions.add(inClause);
return this;
}
public FareQuery between(String field, Object min, Object max) {
conditions.add(field + " BETWEEN ? AND ?");
return this;
}
public String build() {
return "WHERE " + String.join(" AND ", conditions);
}
}
// ์ฌ์ฉ
FareQuery query = new FareQuery()
.in("status", "DRAFT", "SUBMITTED", "PAID") // ๊ฐ๋ณ์ธ์
.in("currency", "KRW") // 1๊ฐ๋ OK
.between("amount", 1000, 100000);
String sql = query.build();
// "WHERE status IN (?,?,?) AND currency IN (?) AND amount BETWEEN ? AND ?"
public class NotificationService {
public void sendToCustomers(String template, Customer... customers) {
for (Customer customer : customers) {
String message = template.replace("{name}", customer.getName());
send(customer, message);
}
}
private void send(Customer customer, String message) {
// ๋ฐ์ก ๋ก์ง
}
}
// ์ฌ์ฉ
service.sendToCustomers("์๋
ํ์ธ์ {name}๋", alice);
service.sendToCustomers("์ด๋ฒคํธ ์๋ด {name}", alice, bob, charlie);
// ๋๋ Customer ๋ฐฐ์ด์ ์ง์
Customer[] vips = customerRepository.findVips();
service.sendToCustomers("VIP {name}๋๊ป", vips); // ๋ฐฐ์ด ์ง์ ์ ๋ฌ OK
// String.format
String msg = String.format("์ด๋ฆ: %s, ๋์ด: %d, ์ง์
: %s", "Alice", 25, "๊ฐ๋ฐ์");
// List.of (Java 9+) โ ๋ถ๋ณ ๋ฆฌ์คํธ
List<String> tags = List.of("urgent", "shipping", "international");
// Arrays.asList
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Stream.of
Stream<String> stream = Stream.of("a", "b", "c");
// String.join
String joined = String.join(", ", "apple", "banana", "cherry");
// "apple, banana, cherry"
// Path.of (Java 11+)
Path p = Path.of("home", "user", "documents", "file.txt");
// Collections.addAll
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d");
โ ์๋ฐ๋ฅผ ์ ์ฐ๋ ค๋ฉด ๊ฐ๋ณ์ธ์๋ ํ์.
// โ ์ปดํ์ผ ์๋ฌ
public void log(String... messages, String level) { ... }
// โ
๋ง์ง๋ง์ ์์น
public void log(String level, String... messages) { ... }
๊ท์น: ๊ฐ๋ณ์ธ์๋ ๋ฌด์กฐ๊ฑด ๋ง์ง๋ง.
// โ ์ปดํ์ผ ์๋ฌ
public void method(String... names, int... ages) { ... }
// โ
๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐ
public void method(String[] names, int[] ages) { ... }
public void method(List<String> names, List<Integer> ages) { ... }
public class Service {
public void process(int x) { System.out.println("int"); }
public void process(int... nums) { System.out.println("varargs"); }
}
Service s = new Service();
s.process(1); // โ ๏ธ "int" ์ถ๋ ฅ โ ์ ํํ ๋งค์นญ์ด ์ฐ์
s.process(1, 2); // "varargs"
s.process(); // "varargs"
๊ท์น โญ :
๊ถ์ฅ: ๊ฐ๋ณ์ธ์์ ์ผ๋ฐ ๋ฉ์๋๋ฅผ ๊ฐ์ ์ด๋ฆ์ผ๋ก ์ค๋ฒ๋ก๋ฉํ์ง ๋ง ๊ฒ.
public void log(Object... args) {
System.out.println("๊ฐ์: " + args.length);
}
String[] arr = {"a", "b", "c"};
logger.log(arr); // โ ๏ธ args.length = 3 (๋ฐฐ์ด์ ์์๋ค)
์?: String[] ์ Object[] ํธํ โ ๋ฐฐ์ด ์์ฒด๊ฐ ๊ฐ๋ณ์ธ์๋ก ํ๋ฆผ.
ํด๊ฒฐ โ ๋ฐฐ์ด์ ํ ์ธ์๋ก ๋ช ์์ ์ ๋ฌ:
logger.log((Object) arr); // ์บ์คํ
์ผ๋ก ๋ฐฐ์ด ์์ฒด๋ฅผ ํ ์ธ์๋ก
// โ args.length = 1, args[0] = arr
โ ํท๊ฐ๋ฆฌ๋ ์ผ์ด์ค. ๋ฉด์ ์์๋ ๊ฐ๋ ์ถ์ .
public void log(String... messages) {
for (String msg : messages) { // messages๊ฐ null์ด๋ฉด NPE!
System.out.println(msg);
}
}
logger.log(null); // ๐ฅ NullPointerException
ํด๊ฒฐ:
public void log(String... messages) {
if (messages == null || messages.length == 0) {
return; // ๋๋ ๊ธฐ๋ณธ ๋์
}
for (String msg : messages) {
if (msg != null) {
System.out.println(msg);
}
}
}
๊ฐ๋ณ์ธ์๋ ํธ์ถํ ๋๋ง๋ค ์ ๋ฐฐ์ด ์์ฑ:
public void log(Object... args) { ... }
// ๋งค ํธ์ถ๋ง๋ค new Object[]{...} ์์ฑ (Heap ์ฌ์ฉ)
for (int i = 0; i < 1000000; i++) {
logger.log("msg", i, "data"); // 100๋ง ๋ฒ์ ๋ฐฐ์ด ์์ฑ
}
๊ณ ์ฑ๋ฅ์ด ํ์ํ ํซ ํจ์ค(hot path)์์๋:
// ์์ฃผ ์ฐ๋ ์ผ์ด์ค ์ค๋ฒ๋ก๋ฉ
public void log(String message) { ... } // 1๊ฐ โ ๋ฐฐ์ด ์์ฑ X
public void log(String m1, String m2) { ... } // 2๊ฐ โ ๋ฐฐ์ด ์์ฑ X
public void log(String.<.. messages) { ... } // N๊ฐ โ ๋ฐฐ์ด ์์ฑ
โ ๋ก๊น ๋ผ์ด๋ธ๋ฌ๋ฆฌ (SLF4J ๋ฑ) ๊ฐ ์ ํํ ์ด ํจํด์ ์ฌ์ฉ.
// โ ๏ธ ์๋๊ฐ ๋ถ๋ช
ํ
public Fare create(String... params) {
// params[0] = customerId
// params[1] = amount
// params[2] = currency
// ... ํธ์ถ์๊ฐ ์์๋ฅผ ์ธ์์ผ ํจ
}
fareService.create("1", "50000", "KRW"); // โ ์๋ ๋ถ๋ช
ํ
ํด๊ฒฐ โ ๊ฐ์ฒด๋ก ๋ช ํํ:
public Fare create(FareCreateRequest request) {
// ๋ช
ํํ ํ๋๋ช
}
fareService.create(new FareCreateRequest(1L, 50000, "KRW"));
โ ๊ฐ๋ณ์ธ์๋ "๋์ง์ ๋ฐ์ดํฐ" ์ผ ๋๋ง. ์๋ฏธ๊ฐ ๋ค๋ฅธ ๊ฐ๋ค์ ๊ฐ์ฒด๋ก.
[Unit 2.1: ๋ฉ์๋์ ๊ตฌ์กฐ]
โ
[Unit 2.2: ๊ฐ๋ณ์ธ์] โ ์ง๊ธ ์ฌ๊ธฐ
โ
[Unit 2.3: ์์๊ณผ ์์ฑ์ ์ฒด์ด๋]
โ
[Unit 2.4: ๋คํ์ฑ]
1์ฃผ์ฐจ ๋ด:
List.of(...), Set.of(...), Arrays.asList(...) ๋ชจ๋ ๊ฐ๋ณ์ธ์Path.of("home", "user", "file") ๊ฐ์ ํจํด๋ฏธ๋ ์ฃผ์ฐจ:
<T> T method(T... values) ๊ฐ์ ์ ๋ค๋ฆญ ๊ฐ๋ณ์ธ์Stream.of(1, 2, 3), Collectors.toMap(...)@RequestMapping(value = {...}, method = {...}) ๊ฐ์ ์ด๋
ธํ
์ด์
๊ฐ๋ณ๊ฐassertThat(list).contains("a", "b", "c") ๊ฐ์ assertion@PathVariable, @RequestParam ์ ๋ค์ค ๊ฐ ์ฒ๋ฆฌ์ธ์ ๋ฌด์์?
| ์ํฉ | ์ถ์ฒ |
|---|---|
| ํธ์ถ ์ ์ธ์๋ฅผ ์ง์ ๋์ด | ๊ฐ๋ณ์ธ์ (method("a", "b", "c")) |
| ์ด๋ฏธ List/๋ฐฐ์ด์ด ์์ | ์ปฌ๋ ์
๋งค๊ฐ๋ณ์ (method(List<String>)) |
| ๋ ๋ค ์์ฃผ ๋ฐ์ | ๊ฐ๋ณ์ธ์ (๋ฐฐ์ด ์ง์ ์ ๋ฌ๋ ๊ฐ๋ฅ) |
| ๋งค์ฐ ๋น๋ฒํ ํธ์ถ (์ฑ๋ฅ ์ค์) | ์ผ๋ฐ ๋งค๊ฐ๋ณ์ ๋๋ ์ค๋ฒ๋ก๋ฉ |
์ ์ฐํ ํจํด โ ๊ฐ๋ณ์ธ์๊ฐ ๋ ๋ค ๋ฐ์:
public void process(String... items) { ... }
process("a", "b", "c"); // ์ง์ ๋์ด
process(myList.toArray(new String[0])); // ๋ฐฐ์ด๋ก ๋ณํ ํ ์ ๋ฌ
| ์ง๋ฌธ | ์ด Unit์์์ ๋ต |
|---|---|
| "๊ฐ๋ณ์ธ์๋?" | ํ์
... ๋ณ์๋ช
ํ์, ๋ฉ์๋ ์์์๋ ๋ฐฐ์ด๋ก ๋ค๋ฃธ |
| "๊ฐ๋ณ์ธ์ ์์น ๊ท์น?" | ๋ง์ง๋ง์ 1๊ฐ๋ง |
| "๋ด๋ถ์ ์ผ๋ก๋?" | ์ปดํ์ผ ์ ๋ฐฐ์ด๋ก ๋ณํ๋๋ syntactic sugar |
| "๋ฐฐ์ด ๋งค๊ฐ๋ณ์์ ์ฐจ์ด?" | ํธ์ถ ํธ์์ฑ (ํธ์ถ์๊ฐ ๋ฐฐ์ด ์ ๋ง๋ค์ด๋ ๋จ) |
| "0๊ฐ ์ธ์๋ ๊ฐ๋ฅ?" | YES โ ๋น ๋ฐฐ์ด๋ก ์ฒ๋ฆฌ๋จ |
1๏ธโฃ ๊ฐ๋ณ์ธ์๋ "๋ฉ์๋์ ์ ๋ ฅ ์ ์ฐ์ฑ์ ์ํ ๋ฌธ๋ฒ์ ์คํ" ์ด๋ค.
ํ์ ... ๋ณ์๋ช์ผ๋ก ์ ์ธํ๋ฉด 0๊ฐ๋ถํฐ N๊ฐ๊น์ง ์์ ๋กญ๊ฒ ์ธ์๋ฅผ ๋ฐ์ ์ ์๋ค. ๋ด๋ถ์ ์ผ๋ก๋ ๋ฐฐ์ด๋ก ๋ณํ ๋๋ฏ๋ก, JVM ์ ์ฅ์์๋ ๊ฐ๋ณ์ธ์์ ๋ฐฐ์ด์ด ์์ ํ ๋์ผํ๋ค. ํธ์ถ์์ ํธ์๋ฅผ ์ํ ๋ฌธ๋ฒ.2๏ธโฃ 2๊ฐ์ง ํต์ฌ ๊ท์น: ๋ง์ง๋ง์, 1๊ฐ๋ง.
๊ฐ๋ณ์ธ์๋ ๋งค๊ฐ๋ณ์ ๋ชฉ๋ก์ ๋ง์ง๋ง ์๋ง ์ฌ ์ ์๊ณ , ๋ฉ์๋๋น 1๊ฐ๋ง ๊ฐ๋ฅํ๋ค. ์ด ๋ ๊ท์น์ ์๋ฐ ์ปดํ์ผ๋ฌ๊ฐ ์ด๋๊น์ง๊ฐ ๊ฐ๋ณ์ธ์์ธ์ง ๊ตฌ๋ณํ๊ธฐ ์ํจ์ด๋ค. ์ด๊ฑธ ์ด๊ธฐ๋ฉด ์ปดํ์ผ ์๋ฌ.
3๏ธโฃ ํธํ์ง๋ง ํจ์ ๋ ์๋ค โ null, ์ฑ๋ฅ, ๋ชจํธํจ.
null์ง์ ์ ๋ฌ ์ NPE, ๋งค ํธ์ถ๋ง๋ค ๋ฐฐ์ด ์์ฑ์ผ๋ก ์ฑ๋ฅ ์ํฅ, ์ค๋ฒ๋ก๋ฉ๊ณผ ์์ผ๋ฉด ๋งค์นญ ์ฐ์ ์์ ํท๊ฐ๋ฆผ. ์๋ฏธ๊ฐ ๋ค๋ฅธ ๊ฐ๋ค์ ๊ฐ์ฒด๋ก ๋ฌถ๋ ๊ฒ ์ข๊ณ , ๋์ง์ ๋ฐ์ดํฐ ์ผ ๋๋ง ๊ฐ๋ณ์ธ์๊ฐ ์์ฐ์ค๋ฝ๋ค.
ํ์
... ๋ณ์๋ช
) ์ ์์ฑํ ์ ์๋คString.format, List.of ๋ฑ ์๋ฐ ํ์ค API์ ๊ฐ๋ณ์ธ์๋ฅผ ํ์ฉํ ์ ์๋คQ1: void log(String... args) ์ void log(String[] args) ์ ์ฐจ์ด๋?
ํธ์ถ ์ธก๋ฉด (๊ฐ์ฅ ํฐ ์ฐจ์ด):
// ๊ฐ๋ณ์ธ์ โ ์์ฐ์ค๋ฌ์
log("a", "b", "c");
// ๋ฐฐ์ด ๋งค๊ฐ๋ณ์ โ ๋งค๋ฒ ๋ฐฐ์ด ์์ฑ ํ์
log(new String[]{"a", "b", "c"});
๋ฉ์๋ ์์์๋ ๋์ผ โ ๋ ๋ค String[] ๋ก ๋ค๋ฃธ:
public void log(String... args) {
System.out.println(args.length); // OK
for (String s : args) { ... } // OK
}
public void log(String[] args) {
System.out.println(args.length); // ๋์ผ
for (String s : args) { ... } // ๋์ผ
}
JVM ๊ด์ :
String[] ์ผ๋ก ๋ณํ๋จACC_VARARGS ํ๋๊ทธ๊ฐ ์ถ๊ฐ๋ ๋ฟ)// โ ์ปดํ์ผ ์๋ฌ โ ๊ฐ์ ์๊ทธ๋์ฒ๋ก ์ธ์
public void log(String... args) { ... }
public void log(String[] args) { ... }
๊ฒฐ๋ก :
Q2: ๊ฐ๋ณ์ธ์๋ ๋งค๊ฐ๋ณ์ ๋ชฉ๋ก์ ์ด๋ ์์น์ ์์ผ ํ๋๊ฐ?
๋ฐ๋์ ๋ง์ง๋ง ์์น.
// โ
OK
public void method(int a, String... rest) { ... }
public void method(int a, double b, Object... rest) { ... }
// โ ์ปดํ์ผ ์๋ฌ
public void method(String... rest, int a) { ... }
public void method(int a, String... rest, int b) { ... }
์?:
method("a", "b", 1) โ "a", "b" ๊ฐ ๊ฐ๋ณ์ธ์? ์๋๋ฉด "a" ๋ง?๋ํ โ ๋ฉ์๋๋น ๊ฐ๋ณ์ธ์๋ 1๊ฐ๋ง:
// โ ์ปดํ์ผ ์๋ฌ
public void method(String... s, int... i) { ... }
๊ฐ์ ์ด์ .
extends, super() ๊ฐ์ ํค์๋์ ์๋ฏธ๊ฐ ๊ถ๊ธํ๋ค