Java import 문에 와일드 카드를 사용하는 것이 왜 좋지 않습니까?


419

다음과 같은 단일 명령문을 사용하는 것이 훨씬 편리하고 깨끗합니다.

import java.awt.*;

개별 클래스를 가져 오는 것보다

import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Canvas;
...

import명령문 에서 와일드 카드를 사용하는 데 어떤 문제가 있습니까?

답변:


518

이것의 유일한 문제는 로컬 네임 스페이스를 혼란스럽게한다는 것입니다. 예를 들어, Swing 앱을 작성 중이므로 need java.awt.Event이 있고 회사의 캘린더 시스템과 인터페이스하고 있다고 가정 해 보겠습니다 com.mycompany.calendar.Event. 와일드 카드 방법을 사용하여 둘 다 가져 오는 경우 다음 세 가지 중 하나가 발생합니다.

  1. java.awt.Event와 사이에 명백한 이름 충돌이 com.mycompany.calendar.Event있으므로 컴파일조차 할 수 없습니다.
  2. 실제로 하나만 가져 오도록 관리 .*하지만 ( 두 가지 가져 오기 중 하나만 수행함 ) 잘못된 것이므로 코드에서 유형이 잘못되었다고 주장하는 이유를 알아내는 데 어려움을 겪습니다.
  3. 코드를 컴파일 할 때 no com.mycompany.calendar.Event가 없지만 나중에 코드를 추가하면 이전에 유효한 코드가 갑자기 컴파일을 중지합니다.

모든 수입을 명시 적으로 나열하는 이점은 사용하려는 클래스를 한 눈에 알 수 있으므로 코드를 훨씬 쉽게 읽을 수 있다는 것입니다. 빠른 일회성 작업을 수행하는 경우 명시 적으로 잘못된 것은 없지만 미래의 관리자는 명확성을 고맙게 생각합니다.


7
일어날 첫 번째 시나리오입니다. 컴파일러는 두 개의 Event 클래스가 있으며 오류를 발생시킵니다.
jan.vdbergh

38
아래 내 의견을 확인하십시오. 시간이 지남에 따라 타사 라이브러리에 유형이 추가되는 데 더 큰 문제가 있습니다. 누군가가 의존하는 항아리에 유형을 추가 한 후에 컴파일을 멈추는 컴파일 코드를 가질 수 있습니다.
Scott Stanchfield

6
문제 1 : 기술적으로는 컴파일 할 수 있지만 매번 정규화 된 클래스 이름을 사용해야합니다.
Kip

1
모든 클래스를 명시 적으로 나열하지 않고도 이러한 종류의 충돌을 해결할 수 있으므로 자체 문제가 발생합니다.
rpjohnst

196

다음 스타 수입에 대한 투표 입니다 . import 문은 클래스가 아닌 패키지 를 가져 오기위한 것 입니다. 전체 패키지를 가져 오는 것이 훨씬 깔끔합니다. 여기에서 식별 된 문제 (예 : java.sql.Datevs java.util.Date)는 다른 방법으로 쉽게 해결할 수 있으며 특정 수입품으로는 실제로 해결되지 않으며 모든 클래스에서 미친 수입품을 정당화하지는 않습니다. 소스 파일을 열고 100 개의 import 문을 페이징해야하는 것보다 더 당혹스러운 일은 없습니다.

특정 가져 오기를 수행하면 리팩토링이 더 어려워집니다. 클래스를 제거하거나 이름을 바꾸면 특정 가져 오기를 모두 제거해야합니다 . 동일한 패키지에서 구현을 다른 클래스로 전환하면 가져 오기를 수정해야합니다. 이러한 추가 단계를 자동화 할 수는 있지만 실질적인 이익을 얻지 못하면 생산성에 영향을 미칩니다.

Eclipse가 기본적으로 클래스 가져 오기를 수행하지 않더라도 모든 사용자는 여전히 스타 가져 오기를 수행합니다. 죄송하지만 특정 수입품에 대한 합리적인 근거는 없습니다.

클래스 충돌을 처리하는 방법은 다음과 같습니다.

import java.sql.*;
import java.util.*;
import java.sql.Date;

28
동의한다. 명시 적 가져 오기를 사용하는 것에 반대하지는 않지만 여전히 스타 가져 오기를 사용하는 것을 선호합니다. "재사용 단위"는 개별 유형이 아니라 전체 패키지라는 점을 강조합니다. 스타 수입에 대해 다른 사람들이 열거 한 이유는 약하며, 스타 수입을 사용한 경험에서 실제로 어려움을 겪지 않았습니다.
Rogério

32
왜 악한 지에 대한 자세한 내용 은 javadude.com/articles/importondemandisevil.html 을 참조하십시오 . 기본 아이디어 :이 코드는 클래스가 될 때 컴파일 중지 될 수 있습니다 추가 가져올 것을 패키지에 (목록이있는 java.util에 추가 할 때처럼 ...)
스콧 Stanchfield

61
언급 한 모든 문제는 최신 IDE (수입 숨기기, 리팩토링 클래스 이름 등)로 해결할 수 있습니다.
assylias 2013

15
소스 코드를 읽거나 쓰는 데 IDE를 사용할 필요는 없습니다. 언어가 엄청나게 브레인 데드가 아니면 특별한 도구없이 코드를 읽을 수 있어야합니다. 이 경우 Java는 훌륭하게 작동합니다. 별 수입을 사용하십시오. 하지 말아야 할 이유가 없습니다.
davetron5000

42
@ davetron5000 코드가 10 + 와일드 카드 수입이 포함되어있는 경우, 당신은 클래스를 사용 Foo하고, (당신의 인수가 내가 하나를 사용하지해야한다는 것입니다 때문에) 내가 IDE를 사용하지 않고 코드를 읽으면, 어떻게 패키지가되는 알 Foo에서 나온 ? 물론 IDE를 사용하면 IDE가 나에게 말할 것이지만, 당신의 모든 주장은 코드없이 코드 를 읽을 수 있어야한다는 것 입니다. 명시 적으로 가져 오기를 수행하면 코드를 문서화하는 데 도움이 되며 (와일드 카드를 피해야하는 큰 이유) IDE 를 사용하지 않고 코드를 작성 하는 것보다 IDE를 사용하지 않고 코드를 읽을 가능성이 훨씬 높습니다 .
Andreas

169

내 기사를 참조하십시오 주문형 가져 오기는 악입니다

간단히 말해서, 가장 큰 문제는 클래스 를 가져올 패키지에 추가 할 때 코드가 손상 될 수 있다는 것 입니다. 예를 들면 다음과 같습니다.

import java.awt.*;
import java.util.*;

// ...

List list;

Java 1.1에서는 이것이 좋았습니다. java.awt에 List가 있으며 충돌이 없습니다.

이제 완벽하게 작동하는 코드를 체크인하고 1 년 후 다른 사람이 코드를 가져와 편집하고 Java 1.2를 사용한다고 가정합니다.

Java 1.2는 List라는 인터페이스를 java.util에 추가했습니다. 팔! 충돌. 완벽하게 작동하는 코드는 더 이상 작동하지 않습니다.

이것은 EVIL 언어 기능입니다. 패키지에 유형이 추가 되어 코드 컴파일이 중단 될 이유 는 없습니다 .

또한 독자가 사용중인 "Foo"를 결정하기가 어렵습니다.


35
이것은 유효한 변명이 아닙니다. Java 버전을 변경하는 경우 코드에서 사용하는 이진 버전을 변경하는 경우에도 실패 할 것으로 예상됩니다. 이 경우 코드에서 컴파일 오류가 발생하고 수정하기가 쉽지 않습니다 (이전 답변 참조 : stackoverflow.com/a/149282/7595 )
Pablo Fernandez

36
@PabloFernandez-Nope-1 년 동안 저장소에있는 코드를 체크 아웃하면 여전히 컴파일해야합니다. 가져온 기존 패키지에 새 클래스를 추가 하면 주문형 가져 오기가 쉽게 실패 할 수 있습니다 . Java 버전을 업그레이드 할 때 문제가 아닙니다. 또한-API가 제대로 설계되면 업그레이드시 기존 코드를 중단 하지 않아야합니다 . Java 버전을 업그레이드 할 때 코드를 변경해야하는 유일한 시간은 주문형 가져 오기 및 Sun이 XML API를 Java 런타임으로 가져 왔기 때문입니다.
Scott Stanchfield 16:25에

3
클래스 경로에 클래스를 추가하면 (유일하고 정규화 된 이름으로!) 영향을 미치지 않습니다. 여기서 포인트는 경우이다 do_not 사용 가져 오기 주문형 구문을, 그것은하지 않습니다. 따라서 불행히도 언어가 허용하는 나쁜 구문을 사용하지 마십시오. 이것은 당신이 맞닥뜨릴 수있는 덜 실제적인 문제 중 하나입니다.
Scott Stanchfield

28
내 대답의 요점은 문제를 일으키는 불필요한 언어 기능이라는 것입니다. 많은 IDE / 편집기가 가져 오기 확장을 자동으로 처리합니다. 정규화 된 가져 오기를 사용하면이 특정 오류가 발생할 가능성이 없습니다. 기존 코드의 버그를 수정 해야하는 압력을 받았을 때이 문제에 부딪 쳤으며 실제 작업에서 방해받는 것과 같은 것이 필요하지 않습니다. java.util.Listvs java.awt.List는 알아 내기에는 나쁘지 않지만 클래스 이름이 Configuration있고 여러 종속성 라이브러리가 최신 maven repo 버전에 추가 했을 때 시도하십시오 .
Scott Stanchfield

5
사용하는 클래스가 API와 호환되는 jar을 업데이트하고 주문형 가져 오기 구문을 사용하지 않으면 나에게 전혀 영향을 미치지 않습니다. 그게 말이 되나요? 수입품 정의에 게으르지 말고 이것은 문제가 아닙니다. 주문형 가져 오기 구문은 Java 언어 정의에서 실수였습니다. 합리적인 언어는 이와 같은 오류를 허용해서는 안됩니다.
Scott Stanchfield

67

그건 하지 자바 import 문에 와일드 카드를 사용하는 것이 나쁜.

에서 클린 코드 로버트 C. 마틴은 실제로 긴 수입 목록을 피하기 위해 그들을 사용하는 것이 좋습니다.

권장 사항은 다음과 같습니다.

J1 : 와일드 카드를 사용하여 긴 가져 오기 목록 피하기

패키지에서 두 개 이상의 클래스를 사용하는 경우

수입품 포장. *;

수입품의 긴 목록은 독자에게 어려워합니다. 우리는 80 개의 수입 라인으로 모듈의 상단을 어수선하게 만들고 싶지 않습니다. 오히려 우리는 수입품이 우리가 어떤 패키지와 협력하는지에 대한 간결한 진술이되기를 원합니다.

와일드 카드 가져 오기는 그렇지 않지만 특정 가져 오기는 어려운 종속성입니다. 클래스를 구체적으로 가져 오는 경우 해당 클래스가 존재해야합니다. 그러나 와일드 카드가 포함 된 패키지를 가져 오는 경우 특정 클래스가 필요하지 않습니다. import 문은 단순히 이름을 찾을 때 검색 경로에 패키지를 추가합니다. 따라서 이러한 가져 오기로 인해 진정한 종속성이 생성되지 않으므로 모듈을 덜 결합시키는 역할을합니다.

특정 가져 오기의 긴 목록이 유용한 경우가 있습니다. 예를 들어 레거시 코드를 처리하고 모의 및 스텁을 빌드하는 데 필요한 클래스를 찾으려면 특정 가져 오기 목록을 내려 모든 클래스의 정규화 된 이름을 찾은 다음 적절한 그루터기. 그러나 특정 수입품에 대한 이러한 사용은 매우 드 rare니다. 또한 대부분의 최신 IDE에서는 단일 명령으로 와일드 카드 가져 오기를 특정 가져 오기 목록으로 변환 할 수 있습니다. 따라서 레거시의 경우에도 와일드 카드를 가져 오는 것이 좋습니다.

와일드 카드 가져 오기로 인해 이름 충돌 및 모호성이 발생할 수 있습니다. 이름은 같지만 패키지가 다른 두 개의 클래스를 특별히 가져 오거나 최소한 사용할 때는 정규화해야합니다. 이것은 성가신 일이 될 수 있지만 와일드 카드 가져 오기를 사용하는 것이 여전히 일반적인 가져 오기보다 낫습니다.


41
Robert C. Martin에게 더 나은 패턴을 사용하여 80 줄의 수입을 필요로하지 않는보다 간결한 패키지와 클래스를 만들 것을 제안합니다. 하나의 클래스 내에서 수입에 필요한 많은 클래스는 단지 '... 제발 휴식, 엔트로피, 엔트로피'를 구걸하고 피하기 가져 오기에 대한 이유를 지적한다는 * '스콧 Stanchfields의 anwers에 설명 s의
레이

28
밥 아저씨가하는 말을 일반적으로 좋아하는 한,이 경우에도 그와 동의하지 않아야합니다.

33
수입품의 긴 목록은 독자에게 어려워합니다. -이 어설 션에 잘못된 추정이 있습니다. 프로그래머는 소스 코드를 위에서 아래로 읽을 필요가 없습니다. 가져 오기 목록을 전혀 읽지 못할 수 있습니다. 그렇게 할 때 설명을 위해 수입품 중 하나만 읽을 수 있습니다. 다른 경우 IDE에서 작업하는 경우 가져 오기가 완전히 축소 될 수 있습니다. 출처에 관계없이 오늘날 이것은 나쁜 조언입니다.
Andy Thomas

10
이 문제에 대해 인용 당국과 관련하여 약간의 균형추를 제공하기 위해 Google Java 스타일 가이드트위터의 Java 스타일 가이드 (대부분 구글에 기반을 둔 공정한)는 와일드 카드 가져 오기를 구체적으로 금지합니다. 그러나 그들은이 결정에 대한 근거를 제공하지 않습니다.
anothernode

1
아마도 Clean Code에 동의하지 않은 유일한 지점 일 것입니다. 몇 줄의 import 문을 스크롤하거나 클래스가 어디에서 왔는지 찾기 위해 고심하고 있습니다. 특정 클래스가 어디에서 왔는지 쉽게 식별하고 싶습니다.
Ganesh Satpute 님이

27

성능 : 바이트 코드가 동일하므로 성능에 영향을 미치지 않습니다. 컴파일 오버 헤드가 발생할 수 있습니다.

컴파일 : 내 개인 컴퓨터에서 가져 오기없이 빈 클래스를 컴파일하는 데 100ms가 걸리지 만 java. *는 170ms가 걸립니다.


3
import java.*아무것도 가져 오지 않습니다. 왜 차이가 있을까요?
Lorne의 후작

6
컴파일하는 동안 검색되므로 차이가 있습니다.
LegendLength

25

네임 스페이스가 복잡해져 모호한 클래스 이름을 완전히 지정해야합니다. 이것의 가장 흔한 발생은 다음과 같습니다.

import java.util.*;
import java.awt.*;

...
List blah; // Ambiguous, needs to be qualified.

또한 모든 종속성이 파일 맨 위에 나열되므로 종속성을 구체적으로 만드는 데 도움이됩니다.


20
  1. 클래스 이름 충돌을 식별하는 데 도움이됩니다. 이름이 같은 다른 패키지의 두 클래스. 이것은 * import로 마스킹 될 수 있습니다.
  2. 의존성을 명시 적으로 만들어서 나중에 코드를 읽어야하는 사람은 가져 오려는 의미와 가져 오지 않은 의미를 알 수 있습니다.
  3. 컴파일러가 전체 패키지를 검색하여 depdencies를 식별 할 필요가 없기 때문에 컴파일 속도를 높일 수 있습니다.
  4. 최신 IDE에서는 명시 적 가져 오기의 불편한 측면이 최소화됩니다. 대부분의 IDE를 사용하면 가져 오기 섹션을 축소 할 수 있으므로 필요한 경우 가져 오기를 자동으로 채우고 사용하지 않는 가져 오기를 자동으로 식별하여 정리할 수 있습니다.

많은 양의 Java를 사용하여 작업 한 대부분의 장소는 명시 적 수입을 코딩 표준의 일부로 만듭니다. 빠른 프로토 타이핑에 여전히 *를 사용한 다음 코드를 제품화 할 때 가져 오기 목록을 확장합니다 (일부 IDE에서도이를 수행함).


나는 당신의 요점 대부분을 좋아하지만, 특히 당신의 대답을 찬성하게 만드는 것은 # 4였습니다. 최신 IDE는 명시 적 가져 오기 사용에 대한 대부분의 주장을 제거합니다 ...
Sheldon R.

어쩌면 여기서 문제의 일부는 표준 Java 라이브러리가 동일한 패키지 내에 많은 클래스로 배치되는 방식입니다. 더 많은 '단일 책임 원칙'을 패키지에 적용하는 것과 반대로.
LegendLength

11

전체 파일을 보지 않고 파일에 사용 된 모든 외부 참조를 볼 수 있기 때문에 특정 가져 오기를 선호합니다. (예, 반드시 정규화 된 참조가 반드시 표시되는 것은 아니라는 것을 알고 있습니다. 그러나 가능하면 항상 참조를 피하십시오.)


9

이전 프로젝트에서 * -imports에서 특정 imports로 변경하면 컴파일 시간이 약 10 분에서 5 분으로 절반으로 단축되었습니다. * -import는 컴파일러가 사용자가 사용한 것과 일치하는 클래스에 대해 나열된 각 패키지를 검색하도록합니다. 이 시간은 작을 수 있지만 큰 프로젝트에는 더해집니다.

* -import의 부작용은 개발자가 필요한 것을 생각하기보다는 일반적인 import line을 복사하여 붙여 넣는 것입니다.


11
이것이 사실이되기 위해서는 많은 수입 라인이나 정말 한심한 개발 시스템 이었을 것입니다. 나는 import- *를 사용한다 . 2 분 안에 2107 개의 클래스로 된 전체 코드베이스 를 컴파일 할 수있다 .
Lawrence Dol

6

DDD 책

구현의 기반이되는 개발 기술에서 모듈 리팩토링 작업을 최소화하는 방법을 찾으십시오. Java에서는 개별 클래스로 가져 오기를 피할 수는 없지만 패키지는 한 번에 전체 패키지를 가져올 수 있으며 패키지 이름을 변경하는 노력을 줄이면서 패키지가 응집력이 높은 단위라는 의도를 반영합니다.

그리고 그것이 로컬 네임 스페이스를 어수선하게 만드는 것은 당신의 잘못이 아닙니다. 패키지의 크기를 비난하십시오.


3
  • 컴파일러가 *를 구체적인 클래스 이름으로 자동 대체하므로 런타임 영향은 없습니다. .class 파일을 디 컴파일하면을 볼 수 없습니다 import ...*.

  • C # 은 패키지 이름 만 사용할 수 있으므로 항상 * (암시 적으로)를 사용합니다 using. 클래스 이름을 전혀 지정할 수 없습니다. Java는 C # 다음에이 기능을 소개합니다. (자바는 여러 측면에서 매우 까다 롭지 만이 주제를 넘어선 것입니다).

  • Intellij Idea에서 "가져 오기 구성"을 수행하면 동일한 패키지의 여러 가져 오기가 *로 자동 대체됩니다. 이 기능은 끌 수 없으므로 필수 기능입니다 (임계 값을 늘릴 수 있음).

  • 수락 된 답변으로 표시된 사례가 유효하지 않습니다. *가 없으면 여전히 같은 문제가 있습니다. * 사용 여부에 관계없이 코드에 pakcage 이름을 지정해야합니다.


1
IntelliJ에서는 필수 기능이 아니며 해제 할 수 있습니다.
Bastien7 '18 .10.19.19

3

레코드의 경우 : 가져 오기를 추가 할 때 종속성도 표시합니다.

동일한 네임 스페이스의 클래스를 제외하고 파일의 종속성이 무엇인지 신속하게 확인할 수 있습니다.


동의하다. 동기 부여자는 성능이나 컴파일이 많지 않지만 사람이 읽을 수있는 코드를 읽을 수 있습니다. 예를 들어 GitHub에서 IDE없이 코드를 읽는다고 상상해보십시오. 읽고있는 파일에 정의되지 않은 모든 참조를 갑자기 찾는 것은 엄청나게 지루합니다.
레오 오리엔 티스

2

가장 중요한 것은 가져 오기 java.awt.*를 통해 프로그램이 향후 Java 버전과 호환되지 않을 수 있다는 것입니다.

"ABC"라는 클래스가 있다고 가정하고 JDK 8을 사용하고 있으며 import를 가져옵니다 java.util.*. 이제 Java 9이 나오고 java.util우연히 "ABC"라고 불리는 새로운 클래스의 패키지 가 있다고 가정하십시오 . 컴파일러는 "ABC"라는 이름으로 자신의 클래스 또는 새 클래스를 의미하는지 알 수 없기 때문에 이제 Java 9에서 컴파일되지 않습니다.java.awt .

java.awt실제로 사용 하는 클래스 만 명시 적으로 가져 오면 문제가 발생하지 않습니다 .

자원:

자바 수입


3
팁 : StreamJava 8의 java.util에서 Java에 추가 된 새 클래스의 예로 사용할 수 있습니다 .
Clint Eastwood

2

양쪽에서 유효한 모든 요점 중에서 와일드 카드를 피해야하는 주된 이유를 찾지 못했습니다. 코드를 읽고 모든 클래스가 무엇인지 또는 해당 정의가 언어 또는 언어로 정의되어 있지 않은지 직접 알고 싶습니다. 파일이있는 위치 둘 이상의 패키지를 가져 오면 * 인식하지 못하는 클래스를 찾기 위해 모든 패키지를 검색해야합니다. 가독성은 최고이며, 코드가 를 읽을 때 IDE가 필요 합니다.


완전히 논리적으로 결론을 내린다면 스타일은 가져 오기를 전혀 사용하지 말고 "new LinkedList"대신 항상 "new java.util.LinkedList"를 사용하고 어디서나 일관되게 수행해야합니다 .
Erwin Smout
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.