2010-01-20 49 views
12

Tôi đánh giá cao bất kỳ điều gì có thể được thực hiện bởi một chuyển đổi, có thể được thực hiện bằng một tuyên bố nếu khác.Khi nào sử dụng câu lệnh chuyển đổi trong Java

Nhưng có các quy tắc phong cách cho thời điểm một người nên sử dụng công tắc thay vì nếu có quy tắc khác.

+3

Khi bạn không muốn peeve-off các nhà phát triển bên cạnh những người có để duy trì mã của bạn 100x nếu/báo cáo khác. –

+6

Tôi không biết ... Tôi nghĩ rằng tôi sẽ chỉ là khó chịu bởi 100 trường hợp trong một chuyển đổi. :-) Mùi giống như thiết kế xấu. – cjstehno

+0

Luôn luôn vì tốc độ nhanh hơn: http://stackoverflow.com/questions/2086529 –

Trả lời

15

Vâng, switch cảm thấy "nhẹ hơn" trong nhiều trường hợp hơn là một bậc thang, theo ý kiến ​​của tôi là if/else if. Về cơ bản bạn không có nhiều cú pháp với niềng răng và dấu ngoặc đơn trong cách mã của bạn. Điều đó đang được nói, switch thừa hưởng cú pháp của C. Điều đó có nghĩa là bạn có break và chỉ một phạm vi duy nhất cho các biến trừ khi bạn giới thiệu các khối mới.

Tuy nhiên, trình biên dịch có thể tối ưu hóa các câu lệnh switch thành bảng tra cứu và thực hiện kiểm tra thời gian biên dịch cho các chữ khi xử lý các liệt kê. Vì vậy, tôi khuyên bạn nên sử dụng switch trên if/else if nếu bạn đang xử lý các loại số hoặc enum.

+2

FWIW, các chuỗi trong câu lệnh chuyển đổi nằm trong JDK7 – basszero

+0

@basszero: Đây có phải là kết quả cuối cùng không? Cuối cùng tôi biết nó vẫn còn được quyết định. – BalusC

4

Bạn sử dụng câu lệnh chuyển đổi khi bạn đang chuyển đổi trên các giá trị khác nhau của các kiểu nguyên thủy/enum/trình bao bọc. (Và không phải tất cả các kiểu nguyên thủy/wrapper, chỉ là các kiểu được hỗ trợ - byte, short, char, int).

Nếu/người khác xử lý phần còn lại.

Ví dụ, nó là hơn thẩm mỹ để nói:

int i = getValueOfI(); 
switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    // do something 
    break; 

, vv

hơn

if (i == 1) { 

} else if (i == 2) { 

}... 

cho một số lượng lớn các trường hợp. Nhưng bạn không thể bật các chuỗi, hoặc các giá trị hàm, hoặc các điều kiện phức tạp, vì vậy nếu bạn cần thực hiện bất kỳ điều nào trong số đó, thì bạn sẽ bị mắc kẹt nếu/else.

+1

Tôi không tin tưởng 100% về mặt thẩm mỹ hơn - loại phụ thuộc vào những gì bạn quen thuộc. Vấn đề với switch là nó rất dễ quên câu lệnh break. – Dan

+1

@Dan: Tôi đã làm cho nó trở thành thói quen luôn gõ 'case' và 'break' theo cặp. Bằng cách đó tôi không bao giờ quên chúng. Nhưng tôi đồng ý rằng nó không quá đẹp. Mặc dù có thể không có nhiều người tranh luận về mặt trận đó :-) – Joey

+0

Đồng ý ít nhất với if-else bạn có cú pháp để giúp bạn - có thể mặc định trong chuyển đổi phải được giải mã sau khi một trường hợp được khớp. – Dan

1

trước hết, câu lệnh chuyển đổi phải có thể sử dụng được. Nếu công tắc được dựa trên giá trị của một biến, thì nó có thể được sử dụng. Nếu nó dựa trên biểu thức boolean phức tạp VÀ/HOẶC/KHÔNG thay đổi cho từng điều kiện, thì bạn hoàn toàn không thể sử dụng nó.

Điều đó đang được nói, nếu có thể áp dụng và có ít nhất 2 trường hợp, khi đó tôi sử dụng nút gạt. Nó dễ dàng mở rộng, dễ đọc và dễ kiểm tra hơn.

0

nếu bạn có quá nhiều điều kiện để kiểm tra loại u tương tự có thể chuyển đổi.

0

Cũng giống như các ngôn ngữ khác như C hoặc C++, báo cáo chuyển đổi hữu ích khi bạn muốn so sánh biến nhất định với danh sách giá trị có thể và thực hiện hành động tùy thuộc vào các giá trị này. Nó là terser hơn nếu phát biểu khác.

5

Tôi sử dụng báo cáo chuyển đổi cho enums, có thể đọc được nếu một câu lệnh if if else if else. Tuy nhiên, bạn nên cố gắng tránh những kiểm tra như vậy trong thiết kế OO.

2

Có hai yếu tố cho tôi:

Độ khó và cho dù bạn muốn quyết định một cái gì đó bằng dãy các giá trị hoặc điều kiện (trong báo cáo chuyển đổi bạn chỉ có thể sử dụng một số nguyên đơn hoặc giá trị được liệt kê).

+0

Đồng ý, bất kỳ thứ gì yêu cầu * dãy * giá trị cần có câu lệnh 'if'. –

3

Cá nhân, tôi sử dụng để tìm cả hai cấu trúc quá ít thủ tục. Mặc dù nó có thể được coi là ngoại lệ OO, tôi sử dụng để sử dụng Bản đồ chứa các phiên bản của giao diện nội bộ cho từng trường hợp của if. Nó cho phép cách ly mã tốt hơn, tôi nghĩ vậy. Tuy nhiên, để bắt nạt trả lời câu hỏi của bạn, tôi chỉ sử dụng các nút chuyển khi tôi có các trường hợp chồng chéo lên nhau (tôi không sử dụng câu lệnh ngắt). Thật không may, nó thực sự không phải là một khối mã duy trì được.

+0

Tôi hoàn toàn đồng ý với quan điểm của bạn về việc sử dụng câu lệnh chuyển đổi cho mã chồng chéo. Trong tâm trí của tôi, các câu lệnh chuyển đổi yêu cầu một cách suy nghĩ hoàn toàn khác với câu lệnh "if..else if". Bạn sử dụng "case :" để chọn điểm bắt đầu của bạn cho một trường hợp trong một khối mã và sau đó bạn sử dụng break để bắt đầu một khối mã hoàn toàn mới. Vấn đề duy nhất cho cách suy nghĩ này đối với tôi, là bạn không thể có các trường hợp trùng lặp cho mỗi "khối phá vỡ" –

-1

Chuyển có hai nhược điểm liên quan:

  • nó được giới hạn trong các loại nguyên thủy và sự đếm
  • bạn phải nhớ "phá vỡ", những gì có thể dẫn đến lỗi không rõ ràng

Thường chuyển đổi là một dấu hiệu cho thiết kế OO kém, bởi vì bạn nên sử dụng đa hình.

Lợi thế duy nhất có thể có của chuyển đổi là nó dễ đọc hơn. Nhưng là

switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    // do something 
    break; 
} 

dễ đọc hơn so này:

if (i == 1) 
    //do something 
else if (i == 2) 
    // do something else 

tôi muốn nói: không! Và bạn sẽ không có những nhược điểm của việc chuyển đổi.

Đề xuất của tôi: Cố gắng tránh chuyển đổi.

+5

Bạn có thể sẽ không thực hiện chuyển đổi chỉ trong hai trường hợp. Thay vì tránh chuyển đổi, học cách sử dụng nó một cách hiệu quả là một ý tưởng tốt hơn, đó chính xác là câu hỏi đang hỏi. – Armstrongest

+0

Hoàn toàn đồng ý, nếu mã OO viết của bạn, bạn sẽ thấy rất ít nhu cầu về các câu lệnh chuyển đổi. –

10

Chuyển có một lợi thế khi nói đến rõ ràng:

switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    case 4: 
    // do something 
    break; 
    case 5: 
    // do something 
    break; 
} 

Nếu mã cho 2 và 4 giống hệt nhau, nó có thể được rõ ràng hơn:

if (i == 1) { 
    // do something 
} 
if ((i == 2) || (i == 4)) { 
    // do something 
} 
if ((i == 5) { 
    // do something 
} 

Nó cũng dễ dàng hơn cho bạn (hoặc một lập trình viên khác) để tách ra các trường hợp và .

+1

Mặt khác, bạn cần đánh vần mọi giá trị đơn cho các trường hợp thông thường thay vì có thể viết 'else if (i% 2 == 0)'. Đôi khi tôi bỏ lỡ 'Chọn trường hợp' của VB :-) – Joey

1

Chuyển đổi và enums.

Nếu giá trị enum bạn đang thử nghiệm hợp pháp có thể là null vì bất kỳ lý do gì, việc đưa nó vào báo cáo chuyển đổi sẽ tạo ra một NullPointerException. Mà không cần nhìn vào mã byte nó là loại khó hiểu rằng nó sẽ làm như vậy.

Giải thích: enums là cú pháp đường được giới thiệu trong 1.5. Câu lệnh switch vẫn hoạt động với các int-ole int, nhưng các giá trị mà nó sử dụng là các phần tử được gán cho enum. Để có được thứ tự, giá trị enum PHẢI là không null.

if statement, mặt khác, sẽ vui lòng chấp nhận null cho giá trị enum và chỉ thất bại khi kiểm tra mà không có NPE.

1

Có lẽ một chút không quan trọng, nhưng nếu tôi trả lời chỉ là câu hỏi trong tiêu đề, thì tôi sẽ nói rằng bạn không nên sử dụng chuyển đổi trong mọi tình huống mà các trường hợp đại diện cho các trạng thái của một số đối tượng. Mô hình nhà nước là giải pháp đẹp hơn nhiều trong những trường hợp đó.

3

tôi muốn đề nghị những quy tắc đơn giản:

Luôn luôn sử dụng một switch khi bạn có ít nhất 2 lựa chọn để phân biệt giữa, khi kiểu dữ liệu là có thể sử dụng cho một công tắc và khi tất cả tùy chọn có giá trị không đổi.

Có ba lý do chính đáng. Thứ nhất, trong hầu hết các trường hợp, switch nhanh hơn một thác if/else. Hai, nó làm cho ý định của mã rõ ràng hơn. Ba, tình trạng khó xử bị lãng quên như vậy gây ra tình trạng khó xử ít hơn nhiều so với các cuộc xâm lược vô cùng khó khăn vì ai đó đã quên mất else.

Máy ảo Java thực sự hỗ trợ hai loại chuyển đổi khác nhau: lệnh chuyển đổi bảng và lệnh tìm kiếm. Các tableswitch được tạo ra bởi trình biên dịch nếu tất cả các hằng số case nằm trong một phạm vi hẹp, nếu không nó sẽ tạo ra một tra cứu. Đối với các câu lệnh switch lớn với nhiều trường hợp, bảng phân loại sẽ hiệu quả hơn so với tra cứu. Các lookupswitch thường được thực hiện bởi một số hình thức tìm kiếm nhị phân.

+0

Hoàn toàn đồng ý ngoại trừ việc quên 'ngắt' và quên' else'. Tôi không nghĩ rằng nó có thể được lập luận rằng một trong những có nhiều khả năng hơn khác – Kirby

1

Tôi luôn thấy rằng câu lệnh chuyển đổi java không mạnh mẽ như tôi cần. Trong số last release lambdaj implements it của bạn với cách sử dụng thông minh là đóng cửa và khớp nối Hamcrest.

Ví dụ: lambdaj Switcher cho phép triển khai mẫu chiến lược. Giả sử bạn phải chuyển đổi giữa ba thuật toán sắp xếp dựa trên một số đặc tính của danh sách cần sắp xếp. Đặc biệt chúng ta hãy giả sử chúng ta đã một thuật toán chuyên cho Strings:

public List<String> sortStrings(List<String> list) { 
    // a sort algorithm suitable for Strings 
} 

một số khác hoạt động tốt với danh sách nhỏ không có hơn 100 mặt hàng:

public List<T> sortSmallList(List<T> list) { 
    // a sort algorithm suitable for no more than 100 items 
} 

và tổng quát hơn mục đích một:

public List<String> sort(List<String> list) { 
    // a generic sort algorithm 
} 

Với 3 phương pháp sắp xếp này, bạn có thể tạo chiến lược lựa chọn phương pháp phù hợp nhất theo cách khai báo sau:

Switcher<List<T>> sortStrategy = new Switcher<List<T>>() 
    .addCase(having(on(List.class).get(0), instanceOf(String.class))), 
     new Closure() {{ of(this).sortStrings(var(List.class)); }}) 
    .addCase(having(on(List.class).size(), lessThan(100))), 
     new Closure() {{ of(this).sortSmallList(var(List.class)); }}) 
    .setDefault(new Closure() {{ of(this).sort(var(List.class)); }}); 

và sắp xếp một danh sách bằng cách sử dụng thuật toán tốt nhất hiện có bằng cách gọi các Switcher:

List<T> sortedList = sortStrategy.exec(list, list); 
+0

+1: Điều này có vẻ mát mẻ. Tôi ngạc nhiên rằng câu lệnh chuyển đổi của Java vẫn còn quá yếu: bạn thậm chí không thể chuyển sang một số nguyên. Biểu hiện trận đấu của Scala mạnh hơn rất nhiều. Heck, câu lệnh EVALUATE của Cobol (19) 85 hữu ích hơn nhiều. –

1

Câu trả lời phụ thuộc vào những gì chính xác bạn đang làm cũng như sự phân bố của các lựa chọn.

Nếu một điều kiện chiếm ưu thế thì nếu/thì thích hợp.

if (i == 1){ 
    //do something 
}else if (i == 2){ 
    // do something else 
} 

Nếu điều kiện được phân phối đồng đều thì tối ưu hóa trong trình biên dịch sẽ mang lại lợi thế về hiệu suất. Sự khác biệt về hiệu suất này trở nên rõ rệt hơn khi số lượng các lựa chọn có thể tăng lên.

switch (i) { 
    case 1: 
    // do something 
    break; 
    case 2: 
    // do something else 
    break; 
    .... 
    case N: 
    // do yet something else 
    break; 
    } 

Điều đó nói rằng, nếu hiệu suất không quan trọng, bạn nên tiếp cận bạn thích nhất có thể đọc được (dễ bảo trì và dễ viết nhất).

Nếu mặt khác, nếu mã của bạn đang ở trong một điểm phát sóng nơi hiệu suất IS quan trọng bạn nên đi với công tắc.

Đối với điều kiện "cực kỳ lớn", ví dụ của Mario về bộ chuyển đổi lambdaj thực sự tuyệt vời và việc quan tâm đến việc khởi tạo sẽ mang lại hiệu suất rất cao. Nó rất giống với mã hóa mà trình tối ưu hóa đang tạo ra.Tôi sẽ định nghĩa "cực kỳ lớn" như khi số lượng các tùy chọn lớn hoặc phức tạp đủ để làm cho nó đáng giá tất cả những gì trong đó, và đáng lo ngại về sự hỗ trợ khi một nhà phát triển đang cố gắng đi qua mã. (Bình luận mã của bạn với lý do tại sao bạn có tất cả điều đó!).

0

Tôi đồng ý với x4u's answer.

Ngoài ra còn có trường hợp khác không được đề cập, tuy nhiên, nơi tôi nghĩ rằng đó là tốt hơn để sử dụng if-else khối chứ không phải là một switch: khi điều kiện bổ sung trong mỗi case khối được thử nghiệm với if khối. Tôi thấy hỗn hợp này tất cả các thời gian trong mã hiện có.

Ví dụ tôi chỉ đi qua mã này mà có một switch trên một chuỗi type và sau đó kiểm tra một chuỗi thứ hai extension sử dụng một if trong cả case báo cáo.

public abstract class Temp { 

    boolean valid; 

    public Temp() { 
     String type = getType(); 
     String extension = getFilenameExtension(); 
     switch(type) { 
      case "Image File": { 
       if(!".jpg".equals(extension) && !".png".equals(extension)) { 
        warnWrongImageFormat(); 
        valid = false; 
       } 
       break; 
      } 
      case "Zip File": { 
       if(!".zip".equals(extension)) { 
        warnWrongZipFormat(); 
        valid = false; 
       } 
       break; 
      } 
      default: { 
       valid = true; 
       break; 
      } 
     } 
    } 

    abstract String getType(); 
    abstract String getFilenameExtension(); 
    abstract void warnWrongImageFormat(); 
    abstract void warnWrongZipFormat(); 
} 

Thay vào đó là rất sạch và ít phức tạp hơn để giảm này một ifelse

public abstract class Temp { 

    boolean valid; 

    public Temp() { 
     String type = getType(); 
     String extension = getFilenameExtension(); 
     valid = true; 
     if("Image File".equals(type) && !".jpg".equals(extension) && !".png".equals(extension)) { 
      warnWrongImageFormat(); 
      valid = false; 
     } 
     else if("Zip File".equals(type) && !".zip".equals(extension)) { 
      warnWrongZipFormat(); 
      valid = false; 
     } 
    } 

    abstract String getType(); 
    abstract String getFilenameExtension(); 
    abstract void warnWrongImageFormat(); 
    abstract void warnWrongZipFormat(); 
} 
Các vấn đề liên quan