Trường hợp sử dụng tốt để nhập tĩnh các phương thức là gì?


137

Chỉ cần nhận xét nhận xét rằng nhập phương thức tĩnh của tôi không phải là một ý tưởng tốt. Nhập tĩnh là một phương thức từ một lớp DA, có hầu hết các phương thức tĩnh. Vì vậy, ở giữa logic kinh doanh, tôi có một hoạt động dường như thuộc về lớp hiện tại:

import static some.package.DA.*;
class BusinessObject {
  void someMethod() {
    ....
    save(this);
  }
} 

Người đánh giá không quan tâm đến việc tôi thay đổi mã và tôi đã không làm nhưng tôi đồng ý với anh ta. Một lý do được đưa ra cho việc không nhập tĩnh là nó gây nhầm lẫn khi phương thức được xác định, nó không thuộc lớp hiện tại và không có trong bất kỳ siêu lớp nào nên quá mất thời gian để xác định định nghĩa của nó (hệ thống đánh giá dựa trên web không thể nhấp được các liên kết như IDE :-) Tôi thực sự không nghĩ vấn đề này, nhập khẩu tĩnh vẫn còn khá mới và tất cả chúng ta sẽ sớm quen với việc định vị chúng.

Nhưng lý do khác, một điều tôi đồng ý, là một cuộc gọi phương thức không đủ tiêu chuẩn dường như thuộc về đối tượng hiện tại và không nên nhảy theo ngữ cảnh. Nhưng nếu nó thực sự thuộc về, nó sẽ có ý nghĩa để mở rộng siêu hạng đó.

Vì vậy, khi nào nó có ý nghĩa với các phương thức nhập tĩnh? Khi nào bạn làm điều đó? Bạn có thích cách các cuộc gọi không đủ tiêu chuẩn không?

EDIT: Ý kiến ​​phổ biến dường như là các phương thức nhập tĩnh nếu không ai sẽ nhầm lẫn chúng là các phương thức của lớp hiện tại. Ví dụ các phương thức từ java.lang.Math và java.awt.Color. Nhưng nếu abs và getAlpha không mơ hồ thì tôi không hiểu tại sao readEmployee lại như vậy. Như trong rất nhiều lựa chọn lập trình, tôi nghĩ đây cũng là một sở thích cá nhân.

Cảm ơn những người trả lời của bạn, tôi đang đóng câu hỏi.


2
Đây là cách sử dụng nhập khẩu tĩnh rất tốt: ibm.com/developerworks/l Library / j
intrepidis

1
@ mr5 cú pháp là import static, tính năng làstatic import
Biến đổi khổ sở

Câu trả lời:


150

Đây là từ hướng dẫn của Sun khi họ phát hành tính năng (nhấn mạnh vào bản gốc):

Vậy khi nào bạn nên sử dụng nhập tĩnh? Rất ít! Chỉ sử dụng nó khi bạn muốn khai báo các bản sao địa phương hoặc lạm dụng quyền thừa kế (Giao diện liên kết không đổi). ... Nếu bạn lạm dụng tính năng nhập tĩnh, nó có thể làm cho chương trình của bạn không thể đọc được và không thể nhận biết được, gây ô nhiễm không gian tên của nó với tất cả các thành viên tĩnh mà bạn nhập. Người đọc mã của bạn (bao gồm cả bạn, một vài tháng sau khi bạn viết nó) sẽ không biết thành viên tĩnh đến từ lớp nào. Nhập tất cả các thành viên tĩnh từ một lớp có thể đặc biệt có hại cho khả năng đọc; nếu bạn chỉ cần một hoặc hai thành viên, hãy nhập chúng riêng lẻ.

( https://docs.oracle.com/javase/8/docs/technotes/guides/lingu/static-import.html )

Có hai phần tôi muốn gọi cụ thể:

  • Sử dụng nhập khẩu tĩnh chỉ khi bạn bị cám dỗ để "thừa kế lạm dụng". Trong trường hợp này, bạn có muốn có BusinessObject extend some.package.DAkhông? Nếu vậy, nhập khẩu tĩnh có thể là một cách xử lý này sạch hơn. Nếu bạn chưa bao giờ mơ ước được gia hạn some.package.DA, thì đây có lẽ là một cách sử dụng kém của nhập khẩu tĩnh. Đừng sử dụng nó chỉ để lưu một vài ký tự khi gõ.
  • Nhập khẩu thành viên cá nhân. Nói import static some.package.DA.savethay DA.*. Điều đó sẽ làm cho nó dễ dàng hơn nhiều để tìm ra phương thức nhập khẩu này đến từ đâu.

Cá nhân, tôi đã sử dụng tính năng ngôn ngữ này rất hiếm khi, và hầu như luôn luôn chỉ với các hằng hoặc enum, không bao giờ với các phương thức. Sự đánh đổi, đối với tôi, gần như không bao giờ có giá trị.


9
Đã đồng ý. Thỉnh thoảng tôi đã sử dụng công cụ nhập khẩu tĩnh, nơi họ thực sự làm cho mã dễ theo dõi hơn đáng kể.
Neil Coffey

65

Một cách sử dụng hợp lý khác cho nhập khẩu tĩnh là với JUnit 4. Trong các phiên bản trước của phương thức JUnit như assertEqualsfailđược kế thừa kể từ khi lớp thử nghiệm mở rộng junit.framework.TestCase.

// old way
import junit.framework.TestCase;

public class MyTestClass extends TestCase {
    public void myMethodTest() {
        assertEquals("foo", "bar");
    }
}

Trong JUnit 4, các lớp kiểm tra không còn cần phải mở rộng TestCasevà thay vào đó có thể sử dụng các chú thích. Sau đó, bạn có thể nhập tĩnh các phương thức khẳng định từ org.junit.Assert:

// new way
import static org.junit.Assert.assertEquals;

public class MyTestClass {
    @Test public void myMethodTest() {
        assertEquals("foo", "bar");
        // instead of
        Assert.assertEquals("foo", "bar");
    }
}

Tài liệu JUnit sử dụng nó theo cách này.


4
Tôi đồng ý. Đơn giản hóa các trường hợp thử nghiệm là một nơi mà ý định không có khả năng bị hiểu lầm.
Bill Michell

6
Chúng tôi đã có điều này trong dự án của chúng tôi và thực sự có vấn đề với những người sử dụng assert () và nghĩ không chính xác rằng nó xuất phát từ việc nhập tĩnh gói Assert. Khi chúng tôi phát hiện ra vấn đề này, một bản quét nhanh cơ sở mã của chúng tôi đã tìm thấy khoảng 30 trường hợp này trong các thử nghiệm của chúng tôi có nghĩa là 30 xác nhận KHÔNG được chạy khi khung kiểm tra được thực thi do cờ DEBUG không được đặt khi chúng tôi chạy thử nghiệm.
Chris Williams

27

Java hiệu quả, Ấn bản thứ hai , ở cuối Mục 19 lưu ý rằng bạn có thể sử dụng nhập tĩnh nếu bạn thấy mình sử dụng nhiều hằng số từ một lớp tiện ích. Tôi nghĩ nguyên tắc này sẽ áp dụng cho nhập khẩu tĩnh cả hằng số và phương thức.

import static com.example.UtilityClassWithFrequentlyUsedMethods.myMethod;

public class MyClass {
    public void doSomething() {
        int foo= UtilityClassWithFrequentlyUsedMethods.myMethod();
        // can be written less verbosely as
        int bar = myMethod();
    }
}

Điều này có lợi thế và bất lợi. Nó làm cho mã dễ đọc hơn một chút với chi phí mất một số thông tin tức thời về nơi phương thức được xác định. Tuy nhiên, một IDE tốt sẽ cho phép bạn đi đến định nghĩa, vì vậy đây không phải là vấn đề.

Bạn vẫn nên sử dụng nó một cách tiết kiệm và chỉ khi bạn thấy mình sử dụng những thứ từ tệp đã nhập nhiều lần.

Chỉnh sửa: Được cập nhật để cụ thể hơn cho các phương thức, vì đó là những gì câu hỏi này đề cập đến. Nguyên tắc áp dụng bất kể những gì được nhập khẩu (hằng số hoặc phương pháp).


1
Câu hỏi của tôi là về các phương thức nhập tĩnh , không phải các trường.
Biến khốn khổ

7
Có lẽ UtilityClassWithFrequentlyUsedMethodscần phải rút ngắn.
Steve Kuo

5
@SteveKuo chắc chắn ít hơn InternalFrameTitlePaneMaxizesButtonWindowNotF FocusedState : P
Anirban Nag 'tintinmj'

@ Rob-Hruska Tôi không thể chỉ bao bọc một phương thức hoặc trường nhập tĩnh trong một phương thức hoặc trường mới nếu tôi dự định sử dụng chúng thường xuyên? Điều đó sẽ cho phép tôi không nhập tĩnh? chẳng hạn như: double myPI = Math.PI;và sau đó tôi có thể tiếp tục tham khảo myPIthay vì Math.PI.
Abdul

@Abdul - Vâng, bạn có thể làm điều đó.
Rob Hruska

14

Tôi đồng ý rằng chúng có thể có vấn đề từ góc độ dễ đọc và nên được sử dụng một cách tiết kiệm. Nhưng khi sử dụng một phương thức tĩnh phổ biến, chúng thực sự có thể tăng khả năng đọc. Ví dụ, trong một lớp kiểm tra JUnit, các phương thức như assertEqualshiển nhiên là chúng đến từ đâu. Tương tự cho các phương thức từ java.lang.Math.


5
Và có gì tệ khi nhìn thấy Math.round (d) so với round (d)?
Steve Kuo

5
@SteveKuo - vì lý do tương tự mà các nhà toán học sử dụng tên biến một chữ cái khi thao tác các công thức: có những lúc tên dài hơn cản trở khả năng đọc của câu lệnh tổng thể. Hãy xem xét một công thức liên quan đến nhiều hàm lượng giác. Một công thức toán học dễ dàng nắm bắt : sin x cos y + cos x sin y. Trong Java trở thành : Math.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y). Kinh khủng khi đọc.
ToolmakerSteve

@ToolmakerSteve, đó là lý do tại sao tôi đã bỏ lỡ usingchỉ thị trong C ++ rất nhiều: chúng có thể là cục bộ .
Franklin Yu

11

Tôi nghĩ rằng nhập tĩnh thực sự hữu ích để loại bỏ các tên lớp dư thừa khi sử dụng các lớp utils như ArraysAssertions.

Không chắc tại sao nhưng Ross đã bỏ qua câu cuối cùng đề cập đến điều này trong tài liệu mà anh ta đang tham khảo .

Được sử dụng một cách thích hợp, nhập tĩnh có thể làm cho chương trình của bạn dễ đọc hơn, bằng cách loại bỏ bản tóm tắt của sự lặp lại tên lớp.

Về cơ bản được sao chép từ blog này: https://medium.com/alphadev-47ts/static-imports-are-great-but-underuse-e805ba9b279f

Ví dụ:

Khẳng định trong các bài kiểm tra

Đây là trường hợp rõ ràng nhất mà tôi nghĩ rằng tất cả chúng ta đều đồng ý

Assertions.assertThat(1).isEqualTo(2);

// Use static import instead
assertThat(1).isEqualTo(2);

Sử dụng các lớp và enums

Tên lớp có thể được loại bỏ trong nhiều trường hợp khi sử dụng các lớp utils làm cho mã dễ đọc hơn

List<Integer> numbers = Arrays.asList(1, 2, 3);

// asList method name is enough information
List<Integer> numbers = asList(1, 2, 3);

Gói java.time có một vài trường hợp nên sử dụng

// Get next Friday from now, quite annoying to read
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));

// More concise and easier to read
LocalDate.now().with(next(FRIDAY));

Ví dụ khi không sử dụng

// Ok this is an Optional
Optional.of("hello world");

// I have no idea what this is 
of("hello world");

10

Tôi sử dụng nó cho màu rất nhiều.

static import java.awt.Color.*;

Rất khó có khả năng màu sắc sẽ bị nhầm lẫn với thứ khác.


1
Đó là một trong những trường hợp sử dụng tốt nhất mà tôi đã thấy khác với trường hợp JUnit / Hamcrest / TestNG cũ.
kevinarpe

3

Tôi khuyên bạn nên sử dụng nhập khẩu tĩnh khi sử dụng OpenGL với Java, đó là một tình huống rơi vào "sử dụng nặng của các hằng số từ một lớp tiện ích" loại

Xem xét điều đó

import static android.opengl.GLES20.*;

cho phép bạn chuyển mã C gốc và viết một cái gì đó có thể đọc được như:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(samplerUniform, 0);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuffer);
glVertexAttribPointer(vtxAttrib, 3, GL_FLOAT, false, 0, 0);

thay vì sự xấu xí phổ biến rộng rãi đó:

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glUniform1i(samplerUniform, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vtxBuffer);
GLES20.glVertexAttribPointer(vtxAttrib, 3, GLES20.GL_FLOAT, false, 0, 0);

2

Nhập khẩu tĩnh là tính năng Java mới duy nhất của Java mà tôi chưa bao giờ sử dụng và không có ý định sử dụng, do các vấn đề bạn vừa đề cập.


Cảm ơn Bombe. Chà, tôi tin rằng họ có ý nghĩa tốt hơn là phải mở rộng và giao diện chỉ chứa một loạt các trận chung kết tĩnh.
Biến khốn khổ

2

Tôi sử dụng 'nhập tĩnh java.lang.Math. *' Khi chuyển mã nặng toán học từ C / C ++ sang java. Các phương thức toán học ánh xạ 1 đến 1 và làm cho việc phân tán mã được chuyển dễ dàng hơn mà không cần chứng nhận tên lớp.


2

Tôi thấy điều này rất thuận tiện khi sử dụng các lớp Utility.

Ví dụ: thay vì sử dụng: if(CollectionUtils.isNotEmpty(col))

Tôi có thể thay thế:

import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
if(isNotEmpty(col))

IMO nào tăng khả năng đọc mã khi tôi sử dụng tiện ích này nhiều lần trong mã của mình.


2

Nói về kiểm tra đơn vị: hầu hết mọi người sử dụng nhập khẩu tĩnh cho các phương thức tĩnh khác nhau mà các khung mô phỏng cung cấp, chẳng hạn như when()hoặc verify().

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Và tất nhiên, khi sử dụng một và chỉ khẳng định bạn nên sử dụng assertThat()nó có ích để nhập tĩnh các trình so khớp hamc bắt buộc, như trong:

import static org.hamcrest.Matchers.*;

1

Chúng rất hữu ích để giảm verbiage, đặc biệt trong trường hợp có rất nhiều phương thức nhập khẩu được gọi và sự khác biệt giữa phương thức cục bộ và phương thức nhập khẩu là rõ ràng.

Một ví dụ: mã liên quan đến nhiều tham chiếu đến java.lang.Math

Một lớp khác: Một lớp trình tạo XML trong đó việc thêm tên lớp cho mọi tham chiếu sẽ ẩn cấu trúc đang được xây dựng


1

Tôi nghĩ rằng nhập khẩu tĩnh là gọn gàng cho NLS theo kiểu gettext.

import static mypackage.TranslatorUtil._;

//...
System.out.println(_("Hello world."));

Cả hai đều đánh dấu chuỗi là một chuỗi phải được trích xuất và cung cấp một cách dễ dàng và rõ ràng để thay thế chuỗi bằng bản dịch của nó.


1

IMO nhập tĩnh là một tính năng khá hay. Hoàn toàn đúng khi sự phụ thuộc nặng nề vào nhập tĩnh khiến mã không thể đọc được và khó hiểu lớp nào thuộc về phương thức tĩnh hoặc thuộc tính. Tuy nhiên, theo kinh nghiệm của tôi, nó trở thành một tính năng có thể sử dụng được, đặc biệt là khi thiết kế Utilcác lớp cung cấp một số phương thức và thuộc tính tĩnh. Sự mơ hồ phát sinh bất cứ khi nào cung cấp nhập khẩu tĩnh có thể được tránh bằng cách thiết lập các tiêu chuẩn mã. Theo kinh nghiệm của tôi trong một công ty, cách tiếp cận này được chấp nhận và làm cho mã sạch hơn và dễ hiểu hơn. Tốt nhất là tôi chèn . Rõ ràng cách tiếp cận này vi phạm các tiêu chuẩn đặt tên của Java nhưng nó cung cấp sự rõ ràng cho mã. Ví dụ: nếu chúng ta có lớp AngleUtils:_ ký tự vào các phương thức tĩnh phía trước và các thuộc tính tĩnh (bằng cách nào đó được thông qua từ C)

public class AngleUtils {

    public static final float _ZERO = 0.0f;
    public static final float _PI   = 3.14f;

    public static float _angleDiff(float angle1, float angle2){

    }

    public static float _addAngle(float target, float dest){

    }
}

Trong trường hợp này, nhập tĩnh cung cấp rõ ràng và cấu trúc mã trông thanh lịch hơn đối với tôi:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

        float initialAngle = _ZERO;
        float angle1, angle2;
        _addAngle(angle1, angle2);
    }
}

Ngay lập tức ai đó có thể cho biết phương thức hoặc thuộc tính nào đến từ một nhập tĩnh và nó ẩn thông tin của lớp mà nó thuộc về. Tôi không đề xuất sử dụng nhập tĩnh cho các lớp là một phần không thể thiếu của mô-đun và cung cấp các phương thức tĩnh và không tĩnh vì trong trường hợp này, điều quan trọng là phải biết lớp nào cung cấp chức năng tĩnh nhất định.


Cảm ơn đã gợi ý đặt tên lại. BTW, một dấu gạch dưới ở phía trước thường được sử dụng trong một số môi trường để đặt tên cho các phương thức / trường riêng. Tôi đang xem xét một quy ước được sửa đổi, chẳng hạn như H_nhập khẩu từ một Helperlớp tiện ích mà tôi có, hoặc C_cho Commonhoặc U_cho Utility. Ngoài ra, tôi đã cân nhắc sử dụng một hoặc hai tên lớp ký tự cho các lớp được sử dụng rộng rãi này, nhưng lo ngại rằng đôi khi chúng có thể xung đột với tên cục bộ - có một số mã kế thừa với tên phương thức chữ hoa.
ToolmakerSteve

-1

Bạn cần sử dụng chúng khi:

  • bạn muốn sử dụng một switchcâu lệnh với các giá trị enum
  • bạn muốn làm cho mã của bạn khó hiểu

9
Đây không phải là sự thật. (1) Bạn có thể sử dụng hằng số enum hoàn toàn tốt mà không cần nhập tĩnh chúng. (2) Nhập khẩu tĩnh của các phương thức lớp JUnit Assert rõ ràng như một tiếng chuông. "AssertTrue (...)" cũng dễ đọc như "Assert.assertTrue (...)", có lẽ là moreso.
Alan Krueger

5
nếu bạn có 5 lần nhập tĩnh trong một lớp 500 dòng, rất khó để biết các phương thức đến từ đâu.
davetron5000

4
+1 cho khi bạn muốn làm cho mã của bạn khó hiểu :)
Biến sai

-5

Tôi sử dụng chúng khi tôi có thể. Tôi có thiết lập IntelliJ để nhắc nhở tôi nếu tôi quên. Tôi nghĩ rằng nó trông sạch sẽ hơn nhiều so với một tên gói đủ điều kiện.


13
Bạn đang nghĩ về việc nhập khẩu thường xuyên. Nhập tĩnh cho phép bạn tham khảo các thành viên của một lớp mà không đủ điều kiện cho họ bằng một tên lớp, ví dụ: nhập tĩnh java.lang.system.out; out.println ("foo"); // thay vì System.out.println ("foo");
sk.

Bây giờ đây là một lời giải thích rất tốt về nhập khẩu tĩnh ... quá tệ Tôi không thể +1 nhận xét
Eldelshell
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.