Có phải việc có một câu lệnh trả về chỉ để đáp ứng thực tiễn xấu về cú pháp?


82

Hãy xem xét đoạn mã sau:

public Object getClone(Cloneable a) throws TotallyFooException {

    if (a == null) {
        throw new TotallyFooException();
    }
    else {
        try {
            return a.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    //cant be reached, in for syntax
    return null;
}

Điều return null;này là cần thiết vì một ngoại lệ có thể bị bắt, tuy nhiên trong trường hợp như vậy vì chúng ta đã kiểm tra xem nó có phải là null hay không (và giả sử chúng ta biết lớp mà chúng ta đang gọi hỗ trợ nhân bản) nên chúng ta biết câu lệnh try sẽ không bao giờ thất bại.

Có phải thực tế không tốt khi đặt thêm câu lệnh return ở cuối chỉ để đáp ứng cú pháp và tránh lỗi biên dịch (với một chú thích giải thích nó sẽ không đạt được), hoặc có cách nào tốt hơn để viết mã một cái gì đó như thế này để bổ sung câu lệnh return là không cần thiết?


25
Phương thức của bạn có một Objecttham số. Nếu akhông phải là một lớp hỗ trợ clonephương thức (hoặc điều này được định nghĩa trong Object?) Hoặc nếu một lỗi xảy ra trong clonephương thức (hoặc bất kỳ lỗi nào khác mà tôi không thể nghĩ ra ngay bây giờ), một Ngoại lệ có thể được ném ra và bạn sẽ đạt được câu lệnh trả về.
Arc676,

26
Giả định của bạn rằng không thể đạt được lợi nhuận cuối cùng là sai. Nó hoàn toàn hợp lệ cho một lớp thực hiện Cloneableđể ném một CloneNotSupportedException. Nguồn: javadocs
Cephalopod

20
Có thể truy cập mã mà bạn đã nhận xét là "không thể đạt được". Như đã đề cập ở trên, có một đường dẫn mã từ CloneNotSupportedException đến dòng này.
David Waterworth

7
Câu hỏi cốt lõi ở đây: Nếu bạn đang giả sử lớp bạn đang gọi hỗ trợ nhân bản, tại sao bạn lại bắt được ngoại lệ? Ngoại lệ nên được ném cho hành vi đặc biệt.
deworde

3
@wero Tôi sẽ đi AssertionErrorthay vì InternalError. Cái thứ hai dường như được sử dụng đặc biệt nếu có gì đó trong JVM gặp sự cố. Và về cơ bản bạn khẳng định rằng mã không đạt được.
kap

Câu trả lời:


134

Một cách rõ ràng hơn mà không có câu lệnh trả lại bổ sung là như sau. Tôi cũng sẽ không bắt CloneNotSupportedException, nhưng hãy để nó cho người gọi.

if (a != null) {
    try {
        return a.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
}
throw new TotallyFooException();

Hầu như lúc nào bạn cũng có thể tìm được thứ tự để kết thúc bằng một cú pháp dễ hiểu hơn những gì bạn có ban đầu.


28
Điều này minh họa một điểm chung thực sự tốt. Nếu bạn thấy mình đang chống lại các yêu cầu của java, điều đó thường có nghĩa là bạn đang cố gắng làm điều gì đó theo cách không tối ưu. Thông thường, nếu tôi cảm thấy không thoải mái, tôi lùi lại và xem xét toàn bộ bức tranh và cố gắng tìm hiểu xem liệu nó có thể được mã hóa theo một cách khác để làm cho nó rõ ràng hơn hay không - nói chung là có. Hầu hết các nitpicks nhỏ của java đều hữu ích một cách đáng kinh ngạc khi bạn nắm bắt được chúng.
Bill K

3
@BillK: Tuy nhiên, mã này có ngữ nghĩa khác nhau, như Maroun Maroun đã chỉ ra.
Deduplicator

5
AssertionErrorlà một lớp được tạo sẵn với mục đích tương tự TotallyFooException(không thực sự tồn tại).
user253751

5
@BillK: "Nếu bạn thấy mình đang chống lại các yêu cầu của java, điều đó thường có nghĩa là bạn đang cố gắng làm điều gì đó theo cách không tối ưu." hoặc Java đã quyết định không hỗ trợ một tính năng giúp cuộc sống của bạn dễ dàng hơn.
user541686,

5
@Mehrdad, ... nhưng nhìn nó theo cách đó không giúp bạn thực sự hữu ích, trừ khi bạn là người làm việc trên RFE cho các phiên bản Java trong tương lai hoặc một ngôn ngữ thay thế. Học các thành ngữ địa phương có lợi ích thực tế, trong khi đổ lỗi cho ngôn ngữ (bất kỳ ngôn ngữ nào, trừ khi ngôn ngữ đó có hỗ trợ lập trình siêu hình a-la-LISP để cho phép tự thêm cú pháp mới) vì không hỗ trợ cách bạn muốn làm điều gì đó ban đầu thì không.
Charles Duffy

101

Nó chắc chắn có thể đạt được. Lưu ý rằng bạn chỉ in stacktrace trong catchmệnh đề.

Trong trường hợp a != nullsẽ có một ngoại lệ, return null sẽ đạt được. Bạn có thể xóa câu lệnh đó và thay thế bằng throw new TotallyFooException();.

Nói chung * , nếu nulllà kết quả hợp lệ của một phương thức (tức là người dùng mong đợi nó và nó có nghĩa là gì đó) thì việc trả về nó dưới dạng tín hiệu cho "dữ liệu không tìm thấy" hoặc ngoại lệ đã xảy ra không phải là một ý kiến ​​hay. Nếu không, tôi không thấy có vấn đề gì tại sao bạn không nên quay lại null.

Lấy ví dụ về Scanner#ioExceptionphương pháp:

Trả về IOExceptionlần cuối cùng được ném bởi Có thể đọc bên dưới của Máy quét này. Phương thức này trả về null nếu không có ngoại lệ nào như vậy tồn tại .

Trong trường hợp này, giá trị trả về nullcó ý nghĩa rõ ràng, khi tôi sử dụng phương thức, tôi có thể chắc chắn rằng tôi nhận được nullchỉ vì không có ngoại lệ nào như vậy và không phải vì phương thức đã cố gắng thực hiện điều gì đó và nó không thành công.

* Lưu ý rằng đôi khi bạn muốn quay lại nullngay cả khi ý nghĩa không rõ ràng. Ví dụ HashMap#get:

Giá trị trả về là null không nhất thiết chỉ ra rằng bản đồ không chứa ánh xạ cho khóa; cũng có thể là bản đồ ánh xạ rõ ràng khóa thành null . Có containsKeythể sử dụng phép toán để phân biệt hai trường hợp này.

Trong trường hợp này, nullcó thể chỉ ra rằng giá trị null đã được tìm thấy và trả về hoặc rằng bản đồ băm không chứa khóa được yêu cầu.


4
Mặc dù điểm của bạn là hợp lệ, nhưng tôi muốn chỉ ra rằng đây chỉ đơn giản là thiết kế API tồi. Cả hai phương thức phải trả về một Optional<Throwable>hoặc Optional<TValue>, tương ứng. Lưu ý: đây không phải là lời chỉ trích về câu trả lời của bạn, mà là về thiết kế API của hai phương pháp đó. (Đó là, tuy nhiên, một thiết kế sai lầm khá phổ biến, đó là phổ biến khắp không chỉ toàn bộ Java API, nhưng ví dụ NET là tốt.)
Jörg W Mittag

4
"crappy" là khá khắc nghiệt nếu bạn không biết liệu Java 8 có thể được sử dụng hay không.
Thorbjørn Ravn Andersen,

2
Nhưng khi nullkhông một giá trị trả về giá trị, trở về nulllà một ý tưởng thậm chí tồi tệ hơn. Nói cách khác, quay trở lại nulltrong một tình huống bất ngờ không bao giờ là một ý kiến ​​hay.
Holger

1
@Holger Ý tôi muốn nói là không hợp lệ là ví dụ: khi người dùng mong đợi nhận được một giá trị từ cấu trúc dữ liệu không thể chứa null, thì nullkhông bao giờ có thể là giá trị trả về "hợp lệ" theo nghĩa là nó phải chỉ ra một cái gì đó khác chứ không phải là một giá trị bình thường giá trị trả về. Trong trường hợp này, việc trả lại nó có một ý nghĩa đặc biệt và tôi không thấy có gì sai trong thiết kế đó (tất nhiên người dùng nên biết cách xử lý tình huống này).
Maroun

1
Đó là điểm: “người dùng nên biết cách xử lý tình huống này” ngụ ý rằng bạn phải chỉ định đó nulllà giá trị trả về có thể có mà người gọi phải xử lý. Điều đó làm cho nó trở thành một giá trị hợp lệ và chúng tôi có cùng cách hiểu về "hợp lệ" ở đây, vì bạn mô tả "hợp lệ" là "người dùng mong đợi nó và nó có nghĩa là gì đó". Nhưng ở đây, tình hình dù sao cũng khác. OP tuyên bố trên mỗi bình luận mã rằng ông khẳng định rằng return null;tuyên bố “không thể đạt được” ngụ ý rằng việc trả về nulllà sai. Thay vào đó, nó nên là một throw new AssertionError();
Holger

26

Có phải thực tế là không tốt khi đặt thêm câu lệnh return ở cuối chỉ để đáp ứng cú pháp và tránh lỗi biên dịch (với một chú thích giải thích nó sẽ không đạt được)

Tôi nghĩ đó return nulllà cách làm không tốt cho ga cuối của một nhánh không thể tiếp cận. Tốt hơn là ném một RuntimeException( AssertionErrorcũng sẽ được chấp nhận) vì đến dòng đó, có gì đó đã xảy ra rất sai và ứng dụng ở trạng thái không xác định. Hầu hết điều này giống như (như ở trên) bởi vì nhà phát triển đã bỏ lỡ một cái gì đó (Các đối tượng có thể không có giá trị nào và không thể sao chép).

Tôi có khả năng không sử dụng InternalErrortrừ khi tôi rất chắc chắn rằng mã không thể truy cập được (ví dụ sau a System.exit()) vì nhiều khả năng tôi mắc lỗi hơn so với VM.

Tôi chỉ sử dụng một ngoại lệ tùy chỉnh (chẳng hạn như TotallyFooException) nếu đến được "dòng không thể truy cập" có nghĩa giống như bất kỳ nơi nào khác mà bạn ném ngoại lệ đó.


3
Như đã lưu ý ở phần khác trong các bình luận, an AssertionErrorcũng có thể là một lựa chọn hợp lý để ném vào một sự kiện "không thể xảy ra" .
Ilmari Karonen

2
Cá nhân tôi muốn ném một SomeJerkImplementedClonableAndStillThrewACloneNotSupportedExceptionException. RuntimeExceptionRõ ràng là nó sẽ kéo dài .
w25r

@ w25r Tôi không sử dụng mã ở trên mà đang trả lời câu hỏi cơ bản. Given Cloneablekhông thực hiện một clone()phương thức công khai và clone()trong đối tượng là protectedđoạn mã trên không thực sự biên dịch.
Michael Lloyd Lee mlk

Tôi sẽ không sử dụng một ngoại lệ tích cực thụ động chỉ trong trường hợp nó thoát ra ngoài (thật buồn cười khi một trong những khách hàng của chúng tôi nhận được một JerkException, tôi không nghĩ nó sẽ được đánh giá cao).
Michael Lloyd Lee mlk

14

Bạn đã nắm bắt được CloneNotSupportedExceptionđiều đó có nghĩa là mã của bạn có thể xử lý nó. Nhưng sau khi nắm bắt được nó, bạn thực sự không biết phải làm gì khi đến cuối hàm, điều này có nghĩa là bạn không thể xử lý nó. Vì vậy, bạn nói đúng rằng nó là một mùi mã trong trường hợp này, và theo quan điểm của tôi có nghĩa là bạn không nên bắt gặp CloneNotSupportedException.


12

Tôi muốn sử dụng Objects.requireNonNull()để kiểm tra xem Tham số a có rỗng không. Vì vậy, rõ ràng khi bạn đọc mã rằng tham số không được rỗng.

Và để tránh các Ngoại lệ được chọn, tôi sẽ ném lại CloneNotSupportedExceptiondưới dạng một RuntimeException.

Đối với cả hai, bạn có thể thêm văn bản đẹp với ý định tại sao điều này không nên xảy ra hoặc đúng như vậy.

public Object getClone(Object a) {

    Objects.requireNonNull(a);

    try {
        return a.clone();
    } catch (CloneNotSupportedException e) {
        throw new IllegalArgumentException(e);
    }

}

7

Trong tình huống này, tôi sẽ viết

public Object getClone(SomeInterface a) throws TotallyFooException {
    // Precondition: "a" should be null or should have a someMethod method that
    // does not throw a SomeException.
    if (a == null) {
        throw new TotallyFooException() ; }
    else {
        try {
            return a.someMethod(); }
        catch (SomeException e) {
            throw new IllegalArgumentException(e) ; } }
}

Thật thú vị khi bạn nói rằng "câu lệnh thử sẽ không bao giờ thất bại", nhưng bạn vẫn gặp khó khăn khi viết một câu lệnh e.printStackTrace();mà bạn khẳng định sẽ không bao giờ được thực hiện. Tại sao?

Có lẽ niềm tin của bạn không được giữ vững. Điều đó là tốt (theo ý kiến ​​của tôi), vì niềm tin của bạn không dựa trên mã bạn đã viết, mà dựa trên kỳ vọng rằng khách hàng của bạn sẽ không vi phạm điều kiện tiên quyết. Tốt hơn để lập trình các phương pháp công khai một cách phòng thủ.

Nhân tiện, mã của bạn sẽ không biên dịch cho tôi. Bạn không thể gọi a.clone()ngay cả khi loại aCloneable. Ít nhất thì trình biên dịch của Eclipse cũng nói như vậy. Biểu thức có a.clone()lỗi

Phương thức clone () không được xác định cho kiểu Cloneable

Những gì tôi sẽ làm cho trường hợp cụ thể của bạn là

public Object getClone(PubliclyCloneable a) throws TotallyFooException {
    if (a == null) {
        throw new TotallyFooException(); }
    else {
        return a.clone(); }
}

Nơi PubliclyCloneableđược xác định bởi

interface PubliclyCloneable {
    public Object clone() ;
}

Hoặc, nếu bạn thực sự cần kiểu tham số Cloneable, thì ít nhất phần sau sẽ biên dịch.

public static Object getClone(Cloneable a) throws TotallyFooException {
//  Precondition: "a" should be null or point to an object that can be cloned without
// throwing any checked exception.
    if (a == null) {
        throw new TotallyFooException(); }
    else {
        try {
            return a.getClass().getMethod("clone").invoke(a) ; }
        catch( IllegalAccessException e ) {
            throw new AssertionError(null, e) ; }
        catch( InvocationTargetException e ) {
            Throwable t = e.getTargetException() ;
            if( t instanceof Error ) {
                // Unchecked exceptions are bubbled
                throw (Error) t ; }
            else if( t instanceof RuntimeException ) {
                // Unchecked exceptions are bubbled
                throw (RuntimeException) t ; }
            else {
                // Checked exceptions indicate a precondition violation.
                throw new IllegalArgumentException(t) ; } }
        catch( NoSuchMethodException e ) {
            throw new AssertionError(null, e) ; } }
}

Lỗi thời gian biên dịch khiến tôi tự hỏi tại sao Cloneablekhông khai báo clonenhư một phương thức công khai không ném bất kỳ ngoại lệ đã kiểm tra nào. Có một cuộc thảo luận thú vị tại bug.java.com/view_bug.do?bug_id=4098033
Theodore Norvell,

2
Tôi muốn đề cập rằng phần lớn mã Java không sử dụng dấu ngoặc nhọn Lisp-esque, và bạn sẽ trông rất bẩn nếu bạn cố gắng sử dụng nó trong môi trường công việc.
Vụ kiện của Fund Monica vào

1
Cảm ơn vì điều đó. Thực ra tôi không kiên định lắm. Tôi đã chỉnh sửa câu trả lời để luôn sử dụng kiểu dấu ngoặc nhọn ưa thích của mình.
Theodore Norvell

6

Các ví dụ trên là hợp lệ và rất Java. Tuy nhiên, đây là cách tôi giải quyết câu hỏi của OP về cách xử lý khoản trả lại đó:

public Object getClone(Cloneable a) throws CloneNotSupportedException {
    return a.clone();
}

Không có lợi ích gì khi kiểm tra axem nó có rỗng hay không. Nó sẽ đến NPE. In dấu vết ngăn xếp cũng không hữu ích. Dấu vết ngăn xếp giống nhau bất kể nó được xử lý ở đâu.

Không có lợi ích gì khi chạy mã với các thử nghiệm rỗng không hữu ích và xử lý ngoại lệ không hữu ích. Bằng cách loại bỏ rác, vấn đề trả lại được tranh luận.

(Lưu ý rằng OP đã bao gồm một lỗi trong việc xử lý ngoại lệ; đây là lý do tại sao cần phải trả lại. OP sẽ không làm sai phương pháp tôi đề xuất.)


5

Có phải việc có một câu lệnh trả về chỉ để đáp ứng thực tiễn xấu về cú pháp?

Như những người khác đã đề cập, trong trường hợp của bạn, điều này thực sự không áp dụng.

Tuy nhiên, để trả lời câu hỏi, các chương trình loại Lint chắc chắn đã không tìm ra điều đó! Tôi đã thấy hai cái khác nhau chống lại vấn đề này trong một tuyên bố chuyển đổi.

    switch (var)
   {
     case A:
       break;
     default:
       return;
       break;    // Unreachable code.  Coding standard violation?
   }

Một người phàn nàn rằng không có thời gian nghỉ là vi phạm tiêu chuẩn mã hóa. Người khác phàn nàn rằng nó là một vì nó là mã không thể truy cập được.

Tôi nhận thấy điều này bởi vì hai lập trình viên khác nhau liên tục kiểm tra lại mã với dấu ngắt được thêm vào rồi loại bỏ, sau đó thêm vào rồi loại bỏ, tùy thuộc vào trình phân tích mã nào họ chạy vào ngày hôm đó.

Nếu bạn rơi vào tình huống này, hãy chọn một và nhận xét điểm bất thường, đó là hình thức tốt mà bạn đã tự thể hiện. Đó là cách tốt nhất và quan trọng nhất.


5

Nó không phải là 'chỉ để đáp ứng cú pháp'. Đó là một yêu cầu về ngữ nghĩa của ngôn ngữ mà mọi đường dẫn mã đều dẫn đến trả lại hoặc ném. Mã này không tuân thủ. Nếu trường hợp ngoại lệ bị bắt, một yêu cầu trả về sau.

Không có 'thực tiễn xấu' về nó, hoặc về việc đáp ứng trình biên dịch nói chung.

Trong mọi trường hợp, dù là cú pháp hay ngữ nghĩa, bạn không có bất kỳ lựa chọn nào về nó.


2

Tôi sẽ viết lại điều này để có sự trở lại vào cuối. Mã giả:

if a == null throw ...
// else not needed, if this is reached, a is not null
Object b
try {
  b = a.clone
}
catch ...

return b

Một lưu ý phụ có liên quan, hầu như bạn luôn có thể cấu trúc lại mã của mình để có câu lệnh trả về ở cuối. Trên thực tế, đây là một tiêu chuẩn viết mã tốt, bởi vì nó giúp cho việc bảo trì và sửa lỗi dễ dàng hơn một chút vì bạn không phải xem xét nhiều điểm thoát. Ngoài ra, nó sẽ là một thực hành tốt để khai báo tất cả các biến ở đầu phương thức, vì lý do tương tự. Cũng nên xem xét rằng việc trả về ở cuối có thể cần khai báo nhiều biến hơn, như trong giải pháp của tôi, tôi cần thêm biến "Đối tượng b".
Pablo Pazos

2

Chưa có ai đề cập đến điều này nên ở đây tiếp tục:

public static final Object ERROR_OBJECT = ...

//...

public Object getClone(Cloneable a) throws TotallyFooException {
Object ret;

if (a == null) 
    throw new TotallyFooException();

//no need for else here
try {
    ret = a.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
    //something went wrong! ERROR_OBJECT could also be null
    ret = ERROR_OBJECT; 
}

return ret;

}

Tôi không thích returnbên trongtry các khối vì lý do đó.


2

Trả về null; là cần thiết vì một ngoại lệ có thể bị bắt, tuy nhiên trong trường hợp như vậy vì chúng tôi đã kiểm tra xem nó có phải là null hay không (và giả sử rằng chúng tôi biết lớp mà chúng tôi đang gọi hỗ trợ nhân bản) nên chúng tôi biết câu lệnh try sẽ không bao giờ thất bại.

Nếu bạn biết chi tiết về các yếu tố đầu vào liên quan theo cách mà bạn biết rằng trycâu lệnh không bao giờ có thể thất bại, bạn có nó thì có ích gì? Tránhtry nếu bạn biết chắc chắn rằng mọi thứ sẽ luôn thành công (mặc dù hiếm khi bạn có thể hoàn toàn chắc chắn trong suốt thời gian tồn tại của codebase).

Trong mọi trường hợp, đáng tiếc là trình biên dịch không phải là trình đọc tâm trí. Nó nhìn thấy chức năng và các đầu vào của nó, và cung cấp thông tin mà nó có, nó cần returncâu lệnh đó ở dưới cùng khi bạn có nó.

Có phải thực tế không tốt khi đặt thêm câu lệnh return ở cuối chỉ để đáp ứng cú pháp và tránh lỗi biên dịch (với một chú thích giải thích nó sẽ không đạt được), hoặc có cách nào tốt hơn để viết mã một cái gì đó như thế này để bổ sung câu lệnh return là không cần thiết?

Hoàn toàn ngược lại, tôi khuyên bạn nên tránh bất kỳ cảnh báo trình biên dịch nào, ví dụ: ngay cả khi điều đó tốn một dòng mã khác. Đừng lo lắng quá nhiều về số lượng dòng ở đây. Thiết lập độ tin cậy của chức năng thông qua thử nghiệm và sau đó tiếp tục. Chỉ giả vờ rằng bạn có thể bỏ qua returncâu lệnh, hãy tưởng tượng quay lại đoạn mã đó một năm sau và sau đó cố gắng quyết định xem returncâu lệnh đó ở phía dưới có gây ra nhầm lẫn hơn một số nhận xét nêu chi tiết về lý do tại sao nó bị bỏ qua vì những giả định bạn có thể thực hiện về các tham số đầu vào. Rất có thểreturn tuyên bố sẽ dễ xử lý hơn.

Điều đó nói, cụ thể về phần này:

try {
    return a.clone();
} catch (CloneNotSupportedException e) {
   e.printStackTrace();
}
...
//cant be reached, in for syntax
return null;

Tôi nghĩ rằng có điều gì đó hơi kỳ lạ với tư duy xử lý ngoại lệ ở đây. Bạn thường muốn nuốt các ngoại lệ tại một trang web mà bạn có điều gì đó có ý nghĩa mà bạn có thể làm để đáp lại.

Bạn có thể try/catchcoi đây là một cơ chế giao dịch. trythực hiện những thay đổi này, nếu chúng không thành công và chúng tôi phân nhánh vào catchkhối, hãy thực hiện việc này (bất cứ thứ gì trong catchkhối) để đáp ứng như một phần của quá trình khôi phục và khôi phục.

Trong trường hợp này, việc chỉ in một stacktrace và sau đó buộc phải trả về null không chính xác là một tư duy giao dịch / khôi phục. Mã chuyển trách nhiệm xử lý lỗi cho tất cả các lệnh gọi mã getCloneđể kiểm tra lỗi theo cách thủ công. Bạn có thể thích nắm bắtCloneNotSupportedException và dịch nó thành một dạng ngoại lệ khác, có ý nghĩa hơn và ném nó đi, nhưng bạn không muốn đơn giản nuốt ngoại lệ và trả về giá trị rỗng trong trường hợp này vì đây không giống như một trang khôi phục giao dịch.

Bạn sẽ kết thúc việc giao trách nhiệm cho người gọi để kiểm tra thủ công và xử lý lỗi theo cách đó, khi đưa ra một ngoại lệ sẽ tránh được điều này.

Nó giống như nếu bạn tải một tệp, đó là giao dịch cấp cao. Bạn có thể có một try/catchở đó. Trong quá trình tryingtải tệp, bạn có thể sao chép các đối tượng. Nếu có lỗi xảy ra ở bất kỳ đâu trong hoạt động cấp cao này (tải tệp), bạn thường muốn ném các ngoại lệ trở lại try/catchkhối giao dịch cấp cao nhất này để bạn có thể khôi phục một cách duyên dáng sau lỗi khi tải tệp (cho dù đó là do lỗi nhân bản hoặc bất cứ điều gì khác). Vì vậy, chúng tôi thường không muốn chỉ nuốt một ngoại lệ ở một số nơi cụ thể như thế này và sau đó trả về giá trị rỗng, ví dụ, vì điều đó sẽ đánh bại rất nhiều giá trị và mục đích của ngoại lệ. Thay vào đó, chúng tôi muốn tuyên truyền các ngoại lệ trở lại một trang web mà chúng tôi có thể xử lý nó một cách có ý nghĩa.


0

Ví dụ của bạn không phải là lý tưởng để minh họa câu hỏi của bạn như đã nêu trong đoạn cuối:

Có phải thực tế không tốt khi đặt thêm câu lệnh return ở cuối chỉ để đáp ứng cú pháp và tránh lỗi biên dịch (với một chú thích giải thích nó sẽ không đạt được), hoặc có cách nào tốt hơn để viết mã một cái gì đó như thế này để bổ sung câu lệnh return là không cần thiết?

Một ví dụ tốt hơn sẽ là việc thực hiện chính bản sao:

 public class A implements Cloneable {
      public Object clone() {
           try {
               return super.clone() ;
           } catch (CloneNotSupportedException e) {
               throw new InternalError(e) ; // vm bug.
           }
      }
 }

Ở đây, mệnh đề bắt không bao giờ được nhập. Vẫn là cú pháp yêu cầu ném một cái gì đó hoặc trả về một giá trị. Vì trả về một cái gì đó không có ý nghĩa, một InternalErrorđược sử dụng để chỉ ra một tình trạng VM nghiêm trọng.


5
Các lớp cha ném một CloneNotSupportedExceptionkhông một "vm lỗi" - đó là hoàn toàn hợp pháp cho một Cloneableđể ném nó. Nó có thể tạo thành một lỗi trong mã của bạn và / hoặc trong lớp cha, trong trường hợp đó, gói nó trong một RuntimeExceptionhoặc có thể là một AssertionError(nhưng không phải InternalError) có thể thích hợp. Hoặc bạn có thể bỏ qua ngoại lệ - nếu lớp cha của bạn không hỗ trợ nhân bản, thì bạn cũng vậy, điều CloneNotSupportedExceptionnày là phù hợp về mặt ngữ nghĩa.
Ilmari Karonen

@IlmariKaronen bạn có thể muốn gửi lời khuyên này để Oracle để họ có thể sửa chữa tất cả các phương pháp nhân bản trong JDK mà ném một InternalError
wero

@wero Chắc chắn ai đó đã có nhưng đống mã kế thừa này.
deworde
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.