시간이 지나면서 이런 생소한 코드들이 점점 이해가 되어가는 과정이 그리워질 것 같음. 처음에 Python을 배우고 나서 Java로 넘어갔을 때 'void'나 'String[]' 같은 타입이 무엇을 의미하는지 몰라서 신기했음. 타입을 배우고 나니 이해가 되었고, 그 다음엔 class와 object 개념, 그리고 main이 왜 static 메서드로 존재하는지 알게 되었음. 더 깊이 들어가면서 이 클래스가 언제 호출되는지도 배우면서 처음엔 단순한 보일러플레이트 같던 코드가 점점 말이 되기 시작했음. 아마 경력이 많은 Java 개발자는 저보다 더 많은 의미를 한 줄에 읽어낼 수 있을 것 같음. 그래도 이제는 없어져서 속 시원함
정말 속 시원함. 지난 30년간 소프트웨어 교육은 개발자에게 '공학'이라는 이름으로 쓸데없는 복잡성을 생산하도록 가르쳐왔음. 예를 들어, A 개발자는 엔트리 포인트나 콜백, 인터페이스 등 무엇이든 필요에 따라 클래스를 만듦. 그 결과 우리는 클래스를 가지게 됨. B 개발자는 클래스에 인스턴스 변수를 추가하며 전체를 변경 가능하게 만들고, 이로 인해 "구현의 진흙탕"이 형성됨. 이후 다른 개발자가 코드 재사용을 위해 상속을 추가하지만, 이로 인해 더 많은 복잡성과 동적 디스패치 악몽이 생김. 결국 참조 순환(rference cycle)이 생겨 객체들의 연결관계가 꼬이게 됨. 시간이 지날수록 리팩토링은 점점 힘들고 귀찮아져서, 결국 코드를 지우고 새로 시작하게 됨
대학에서 처음 프로그래밍을 배울 때 이 코드를 보고 교수님이 "보통은 코드 설명을 다 해주겠지만, 이 부분은 그냥 일단 받아들이라"고 했던 기억이 있음. 나중에 돌아보니 내가 저 코드를 다 이해하고 있다는 걸 깨달았을 때 꽤 멋졌던 경험임. 그래도 이제 시대가 바뀌는 것 같아서 속이 후련함
사실 static 클래스 메서드는 프로그램의 단일 진입점(entry point)이 반드시 클래스와 관련되어야 한다는 현실 부정에 가까움. C++ 뿐만 아니라 Python, Ruby처럼 절차지향의 현실을 인정한 언어도 있는데, Java는 마치 사용자의 눈을 가리고 "완벽한 OOP 세계"로 몰아넣는 듯함
구식 class 기반 방식을 알던 입장에서 새로운 스타일(Java 21의 unnamed classes 등)이 더 질문을 던지는 것 같음. 정말 "모든 것이 객체"인 Java를 절차지향 언어로 바꾼 건지 의문임. 만약 커맨드라인 인자가 필요할 때는 어떻게 해야 하는지 궁금함. 나는 주로 Java를 피해서 잘 몰랐지만, 이건 진심 궁금해서 묻는 질문임
Java 1.2 시절에는 표준입력을 다음과 같이 읽었음. Scanner class는 처음이라 생소함
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String input = in.readLine();
나도 비슷한 경험임. 오래전에 Java를 하다 보니 public static void main 패턴은 익숙했는데, Scanner 사용법이 어색해서 이유를 정확히 이해하지 못했었음
내 경험상 대형 프로젝트에서 수십 년 동안 일해왔기 때문에, main 함수가 어떻게 써있는지는 내 일상이나 커리어에는 전혀 영향을 끼치지 않음. 여러분은 이런 변화가 실제로 얼마나 영향을 주는지 궁금함
Android 개발자라면 main이란 개념 자체가 없었음. 솔직히 trivial한 Hello World의 구현이 못생겼다고 해서 그 언어가 그 이상을 잘 처리하는지의 척도가 된다고 생각하지 않음
많은 사람들이 Java로 프로그래밍 입문을 하면서 main을 수십 번 타이핑했지만, 그게 무슨 의미인지 전혀 모르고 있었음
실무 개발자에게는 크게 영향이 없지만, 초보자에게는 중요한 문제임. 예전에 Java를 가르쳤는데, "Hello, World"를 찍기 전에 외워야 하는 마법 주문(보일러플레이트)이 학생들에겐 진입장벽으로 작동했음
명령줄 인터페이스를 만든다면 영향이 있을 수 있겠지만, Java로 CLI를 만드는 경우 그리 많지 않음
나는 최근 5년 정도 codebase에서 main을 본 적이 없음. 그리고 거의 새로운 클래스를 직접 만들기보다는 보통 프레임워크 특정 클래스를 구현하거나 확장하는 형태임
아쉽게도 이 기능이 단일 파일 프로그램에서만 쓸 수 있도록 나온 점이 아쉬움. 패키지 레벨 메소드나 여러 클래스/레코드를 파일에 둘 수 있게 허용했다면 Java가 더 좋아졌을 것 같음. 하지만 어쨌든 "Java 코드를 일반적으로 작성하는 방식에 미치는 영향"은 원하지 않았던 것 같음
30년만에 Java가 마침내 꽤 괜찮은 언어로 진화하는 모습이 놀라움
나는 프레임워크 없이 순수하게 Java와 C++로 많은 코드를 짰었고, 이 때는 보일러플레이트가 훨씬 적고 간단했음. 진짜 편리함을 체감한 건 약 10년 전 lambda가 추가됐을 때임. 람다 도입 이후 코드의 분량이 줄어들고, 굉장히 쾌적해짐을 느꼈음. Java는 오래도록 엄청나게 명시적이고, 사소한 것에도 오타 체크만 해주는 것 같은 불필요한 반복문이 많았음. Kotlin이 뜨기 전까지 이런 분위기였는데, 이후 lambda와 익명클래스 등으로 개발 경험이 크게 나아졌음
Java가 끔찍했던 언어라는 주장은 너무 과장임
반면 Python 같은 언어는 30년이 지났어도 아직도 불편하다고 생각함
아마도 정말 나쁜 언어를 경험해 본 적이 없거나 "끔찍하지 않음"의 기준이 굉장히 높다고 추측함
그래도 Java는 뛰어난 하위호환성과 패키지 관리 시스템이 있다는 점은 대단함
이전 댓글에서 언급한 내용인데, Java 개발자로서 python은 오히려 이상하지만 지금 방식이 나쁘다고 생각하지 않음. 다만 추상화의 기본을 이해하지 못한 채 바로 프로그래밍을 하면 "프로그래밍 입문"에는 좋은 선택이 아닐 수 있다고 봄. 어떤 도구가 목적 지향, 패러다임 지향 등으로 설계되면 때로는 극단적 추상화가 일어날 수 있음. Java는 OOP에 초점을 두고 설계되어있기 때문에 인터페이스 같은 블록 단위 사고가 자연스러움. 메서드/클래스의 접근제한자 역시 누구를 위한 것인지에 따라 명확히 구분함(public, protected, default, private 등). 즉, Java로의 입문은 사용자(프로그래머)를 향한 interface의 노출로부터 시작함. 이상해 보일 순 있지만, 적어도 일관성은 있음
무거운 문법은 OOP와 직접적인 상관이 없음. Java가 등장하기 전 OOP의 어떤 정의도 "함수는 클래스 밖에 존재할 수 없다"라고 말하지 않았음. Sun은 오랫동안 스탠드얼론 함수 개념을 거부했고(정적 import는 Java 5, 클로저는 Java 8, compact source 파일/instance main은 이제서야 도입), 그 결과 모든 함수가 여전히 클래스 내부에 있음(실용성과 호환성 때문이지, 철학적 이유는 아님). "모든 것이 객체"를 고집하려 했지만, 실제로는 primitive 값을 도입하는 모순도 있었음. 함수가 반드시 클래스 내부에 있어야 할 실용적 이득은 없음. 오히려 정수와 같은 값에 메서드를 정의할 수 있었으면 더 좋았을 것임. 참고로 Smalltalk 같은 "순수 OOP" 언어에서는 클래스 밖에 함수를 자유롭게 정의할 수 있고 REPL에서도 직접 코드를 실행할 수 있음 관련 링크
Hello World가 중요한 이유는, 프로그램 실행을 위한 보일러플레이트와 실제 출력 코드를 동시에 보여줄 수 있기 때문임. 또한 빌드 도구 설정이라는 측면에서도, 일부는 '보일러플레이트'라기보다는 실습/도구 설정을 위한 절차임. 이를 바로 설명해주면 큰 문제는 아님
만약 컴파일러 트릭으로 내부에서 클래스를 생성한다면 그저 보여주기식 변화에 불과함. 다른 top-level 함수가 허용되지 않는다면 결국 특이 케이스일 뿐이라 여전히 어색함
C#은 9.0부터 top level statement를 지원하지만, 결국 내부적으로는 정적 메서드로 변환됨. 여러 top level 함수도 비슷하게 동작함. 실제 예시: C# decompiled example
이런 트릭은 실제로 유용한 컴파일러 변환의 예시임(closure, async state machine 등). 이번 변화도 그런 셈임
C/C++에서 main()에서만 기본 return 0이 적용되는 것도 비슷하게 어색하게 느껴짐
만약 main만 top-level 함수로 허용되고, 다른 것은 불가능하다면 이 역시 별로 좋은 변화가 아니라고 봄
Java 코드 예제에서 import 라인을 왜 생략하는지 이해가 안됨. 보일러플레이트의 복잡함을 보여주려면 import도 반드시 써줘야 할 것 같음
보통 IDE가 import를 자동 관리해주기 때문에 신경쓰지 않음. IO 관련 클래스라면 compact 예제에선 따로 import가 필요 없어서 실제로 코드가 저대로 동작함
대부분의 Java IDE는 import를 자동으로 관리해줌
public static void main(String[] args)라는 엔트리포인트를 C#처럼 top-level statement 패러다임으로 추상화하면 보일러플레이트를 줄이고 코드가 간결해질 것 같은데, 왜 그렇게 안 했는지 궁금함
엔트리 포인트가 특수한 예외 케이스로 존재한다면 이미 OOP의 한계를 인정하는 셈임. 그렇다면 과감하게 새로운 대안을 설계하는 편이 낫지 않을까 생각함
unnamed class 방식을 활용하면 전역 변수 구현이 쉬워지고, 핫리로드도 가능하면서 문법이 간결해진다는 점이 좋다고 생각함. 한편, Java 파일은 이름이 public class와 동일해야 한다는 제약이 있어서, 차라리 public class X { } 부분을 옵션으로 두고 필요한 경우만 쓰게 했어도 됐을 듯함. 왜 익명 클래스를 도입해야 했는지 잘 이해가 안 됨
컴파일러는 여전히 HelloWorld.class 같은 클래스로 변환하므로, 그 이름은 구현 세부사항일 뿐 실제로 소스코드에선 직접적으로 쓸 순 없음 https://openjdk.org/jeps/445
Hacker News 의견
시간이 지나면서 이런 생소한 코드들이 점점 이해가 되어가는 과정이 그리워질 것 같음. 처음에 Python을 배우고 나서 Java로 넘어갔을 때 'void'나 'String[]' 같은 타입이 무엇을 의미하는지 몰라서 신기했음. 타입을 배우고 나니 이해가 되었고, 그 다음엔 class와 object 개념, 그리고 main이 왜 static 메서드로 존재하는지 알게 되었음. 더 깊이 들어가면서 이 클래스가 언제 호출되는지도 배우면서 처음엔 단순한 보일러플레이트 같던 코드가 점점 말이 되기 시작했음. 아마 경력이 많은 Java 개발자는 저보다 더 많은 의미를 한 줄에 읽어낼 수 있을 것 같음. 그래도 이제는 없어져서 속 시원함
정말 속 시원함. 지난 30년간 소프트웨어 교육은 개발자에게 '공학'이라는 이름으로 쓸데없는 복잡성을 생산하도록 가르쳐왔음. 예를 들어, A 개발자는 엔트리 포인트나 콜백, 인터페이스 등 무엇이든 필요에 따라 클래스를 만듦. 그 결과 우리는 클래스를 가지게 됨. B 개발자는 클래스에 인스턴스 변수를 추가하며 전체를 변경 가능하게 만들고, 이로 인해 "구현의 진흙탕"이 형성됨. 이후 다른 개발자가 코드 재사용을 위해 상속을 추가하지만, 이로 인해 더 많은 복잡성과 동적 디스패치 악몽이 생김. 결국 참조 순환(rference cycle)이 생겨 객체들의 연결관계가 꼬이게 됨. 시간이 지날수록 리팩토링은 점점 힘들고 귀찮아져서, 결국 코드를 지우고 새로 시작하게 됨
대학에서 처음 프로그래밍을 배울 때 이 코드를 보고 교수님이 "보통은 코드 설명을 다 해주겠지만, 이 부분은 그냥 일단 받아들이라"고 했던 기억이 있음. 나중에 돌아보니 내가 저 코드를 다 이해하고 있다는 걸 깨달았을 때 꽤 멋졌던 경험임. 그래도 이제 시대가 바뀌는 것 같아서 속이 후련함
사실 static 클래스 메서드는 프로그램의 단일 진입점(entry point)이 반드시 클래스와 관련되어야 한다는 현실 부정에 가까움. C++ 뿐만 아니라 Python, Ruby처럼 절차지향의 현실을 인정한 언어도 있는데, Java는 마치 사용자의 눈을 가리고 "완벽한 OOP 세계"로 몰아넣는 듯함
구식 class 기반 방식을 알던 입장에서 새로운 스타일(Java 21의 unnamed classes 등)이 더 질문을 던지는 것 같음. 정말 "모든 것이 객체"인 Java를 절차지향 언어로 바꾼 건지 의문임. 만약 커맨드라인 인자가 필요할 때는 어떻게 해야 하는지 궁금함. 나는 주로 Java를 피해서 잘 몰랐지만, 이건 진심 궁금해서 묻는 질문임
Java 1.2 시절에는 표준입력을 다음과 같이 읽었음. Scanner class는 처음이라 생소함
나도 비슷한 경험임. 오래전에 Java를 하다 보니 public static void main 패턴은 익숙했는데, Scanner 사용법이 어색해서 이유를 정확히 이해하지 못했었음
내 경험상 대형 프로젝트에서 수십 년 동안 일해왔기 때문에, main 함수가 어떻게 써있는지는 내 일상이나 커리어에는 전혀 영향을 끼치지 않음. 여러분은 이런 변화가 실제로 얼마나 영향을 주는지 궁금함
Android 개발자라면 main이란 개념 자체가 없었음. 솔직히 trivial한 Hello World의 구현이 못생겼다고 해서 그 언어가 그 이상을 잘 처리하는지의 척도가 된다고 생각하지 않음
많은 사람들이 Java로 프로그래밍 입문을 하면서 main을 수십 번 타이핑했지만, 그게 무슨 의미인지 전혀 모르고 있었음
실무 개발자에게는 크게 영향이 없지만, 초보자에게는 중요한 문제임. 예전에 Java를 가르쳤는데, "Hello, World"를 찍기 전에 외워야 하는 마법 주문(보일러플레이트)이 학생들에겐 진입장벽으로 작동했음
명령줄 인터페이스를 만든다면 영향이 있을 수 있겠지만, Java로 CLI를 만드는 경우 그리 많지 않음
나는 최근 5년 정도 codebase에서 main을 본 적이 없음. 그리고 거의 새로운 클래스를 직접 만들기보다는 보통 프레임워크 특정 클래스를 구현하거나 확장하는 형태임
JEP 445: Unnamed Classes and Instance Main Methods가 Java 21에 릴리스됨
https://openjdk.org/jeps/445
30년만에 Java가 마침내 꽤 괜찮은 언어로 진화하는 모습이 놀라움
나는 프레임워크 없이 순수하게 Java와 C++로 많은 코드를 짰었고, 이 때는 보일러플레이트가 훨씬 적고 간단했음. 진짜 편리함을 체감한 건 약 10년 전 lambda가 추가됐을 때임. 람다 도입 이후 코드의 분량이 줄어들고, 굉장히 쾌적해짐을 느꼈음. Java는 오래도록 엄청나게 명시적이고, 사소한 것에도 오타 체크만 해주는 것 같은 불필요한 반복문이 많았음. Kotlin이 뜨기 전까지 이런 분위기였는데, 이후 lambda와 익명클래스 등으로 개발 경험이 크게 나아졌음
Java가 끔찍했던 언어라는 주장은 너무 과장임
반면 Python 같은 언어는 30년이 지났어도 아직도 불편하다고 생각함
아마도 정말 나쁜 언어를 경험해 본 적이 없거나 "끔찍하지 않음"의 기준이 굉장히 높다고 추측함
그래도 Java는 뛰어난 하위호환성과 패키지 관리 시스템이 있다는 점은 대단함
이전 댓글에서 언급한 내용인데, Java 개발자로서 python은 오히려 이상하지만 지금 방식이 나쁘다고 생각하지 않음. 다만 추상화의 기본을 이해하지 못한 채 바로 프로그래밍을 하면 "프로그래밍 입문"에는 좋은 선택이 아닐 수 있다고 봄. 어떤 도구가 목적 지향, 패러다임 지향 등으로 설계되면 때로는 극단적 추상화가 일어날 수 있음. Java는 OOP에 초점을 두고 설계되어있기 때문에 인터페이스 같은 블록 단위 사고가 자연스러움. 메서드/클래스의 접근제한자 역시 누구를 위한 것인지에 따라 명확히 구분함(public, protected, default, private 등). 즉, Java로의 입문은 사용자(프로그래머)를 향한 interface의 노출로부터 시작함. 이상해 보일 순 있지만, 적어도 일관성은 있음
무거운 문법은 OOP와 직접적인 상관이 없음. Java가 등장하기 전 OOP의 어떤 정의도 "함수는 클래스 밖에 존재할 수 없다"라고 말하지 않았음. Sun은 오랫동안 스탠드얼론 함수 개념을 거부했고(정적 import는 Java 5, 클로저는 Java 8, compact source 파일/instance main은 이제서야 도입), 그 결과 모든 함수가 여전히 클래스 내부에 있음(실용성과 호환성 때문이지, 철학적 이유는 아님). "모든 것이 객체"를 고집하려 했지만, 실제로는 primitive 값을 도입하는 모순도 있었음. 함수가 반드시 클래스 내부에 있어야 할 실용적 이득은 없음. 오히려 정수와 같은 값에 메서드를 정의할 수 있었으면 더 좋았을 것임. 참고로 Smalltalk 같은 "순수 OOP" 언어에서는 클래스 밖에 함수를 자유롭게 정의할 수 있고 REPL에서도 직접 코드를 실행할 수 있음
관련 링크
Hello World가 중요한 이유는, 프로그램 실행을 위한 보일러플레이트와 실제 출력 코드를 동시에 보여줄 수 있기 때문임. 또한 빌드 도구 설정이라는 측면에서도, 일부는 '보일러플레이트'라기보다는 실습/도구 설정을 위한 절차임. 이를 바로 설명해주면 큰 문제는 아님
만약 컴파일러 트릭으로 내부에서 클래스를 생성한다면 그저 보여주기식 변화에 불과함. 다른 top-level 함수가 허용되지 않는다면 결국 특이 케이스일 뿐이라 여전히 어색함
C#은 9.0부터 top level statement를 지원하지만, 결국 내부적으로는 정적 메서드로 변환됨. 여러 top level 함수도 비슷하게 동작함. 실제 예시: C# decompiled example
이런 트릭은 실제로 유용한 컴파일러 변환의 예시임(closure, async state machine 등). 이번 변화도 그런 셈임
C/C++에서 main()에서만 기본 return 0이 적용되는 것도 비슷하게 어색하게 느껴짐
만약 main만 top-level 함수로 허용되고, 다른 것은 불가능하다면 이 역시 별로 좋은 변화가 아니라고 봄
Java 코드 예제에서 import 라인을 왜 생략하는지 이해가 안됨. 보일러플레이트의 복잡함을 보여주려면 import도 반드시 써줘야 할 것 같음
보통 IDE가 import를 자동 관리해주기 때문에 신경쓰지 않음. IO 관련 클래스라면 compact 예제에선 따로 import가 필요 없어서 실제로 코드가 저대로 동작함
대부분의 Java IDE는 import를 자동으로 관리해줌
public static void main(String[] args)라는 엔트리포인트를 C#처럼 top-level statement 패러다임으로 추상화하면 보일러플레이트를 줄이고 코드가 간결해질 것 같은데, 왜 그렇게 안 했는지 궁금함
Paving the on-ramp 문서를 참고하길 바람
엔트리 포인트가 특수한 예외 케이스로 존재한다면 이미 OOP의 한계를 인정하는 셈임. 그렇다면 과감하게 새로운 대안을 설계하는 편이 낫지 않을까 생각함
unnamed class 방식을 활용하면 전역 변수 구현이 쉬워지고, 핫리로드도 가능하면서 문법이 간결해진다는 점이 좋다고 생각함. 한편, Java 파일은 이름이 public class와 동일해야 한다는 제약이 있어서, 차라리
public class X { }부분을 옵션으로 두고 필요한 경우만 쓰게 했어도 됐을 듯함. 왜 익명 클래스를 도입해야 했는지 잘 이해가 안 됨컴파일러는 여전히 HelloWorld.class 같은 클래스로 변환하므로, 그 이름은 구현 세부사항일 뿐 실제로 소스코드에선 직접적으로 쓸 순 없음
https://openjdk.org/jeps/445