2013-07-25 37 views
26

Có cách nào trong java, để tạo ra một phương thức, đó là mong đợi hai varargs khác nhau? Tôi biết, với cùng một loại đối tượng, điều đó là không thể vì trình biên dịch không biết bắt đầu từ đâu hoặc kết thúc ở đâu. Nhưng tại sao nó cũng không thể với các loại đối tượng khác nhau?Java hai varargs trong một phương pháp

Ví dụ:

public void doSomething(String... s, int... i){ 
    //... 
    //... 
} 

Có cách nào để tạo ra phương pháp như thế này? Cảm ơn bạn!

+3

Bạn không thể làm điều đó: trình biên dịch biết đâu là danh sách phụ đầu tiên kết thúc và phiên bản thứ hai bắt đầu? – dasblinkenlight

+3

Tôi tò mò về cách sử dụng cho –

+1

@dasblinkenlight Một tập hợp các arg này thuộc loại 'String' trong khi cái còn lại thuộc loại' int'; trình biên dịch có thể phỏng đoán nơi kết thúc đầu tiên và thứ hai bắt đầu, phải không? – arshajii

Trả lời

29

Chỉ một vararg, xin lỗi. Nhưng sử dụng asList() làm cho nó gần như là thuận tiện:

public void myMethod(List<Integer> args1, List<Integer> args2) { 
    ... 
} 

----------- 

import static java.util.Arrays.asList; 
myMethod(asList(1,2,3), asList(4,5,6)); 
+0

Tôi không thích điều này vì bất kỳ quá trình xử lý nào trên args1 hoặc args2 sẽ yêu cầu truyền. Lộn xộn. –

+0

Nếu bạn biết loại chung, sử dụng nó - sau đó không có đúc. Ví dụ trên sẽ là "tương đương" với phương pháp (Đối tượng ...): myMethod (Danh sách args1, Danh sách args2) sẽ hoạt động mà không được truyền trong ví dụ trên. – rec

+0

@WilliamMorrison - Không yêu cầu truyền nếu bạn sử dụng loại tham chiếu thực tế (ví dụ: 'Số nguyên') thay vì' '. – DaoWen

17

Trong Java, chỉ một đối số varargs được phép và nó phải là tham số cuối cùng của chữ ký.

Nhưng tất cả nó có phải nó chuyển nó sang một mảng dù sao, vì vậy bạn chỉ nên làm hai tham số mảng rõ ràng của bạn:

public void doSomething(String[] s, int[] i){ 
+0

Cảm ơn bạn! Đúng vậy, nhưng hai mảng cố định nổi bật trong trường hợp của tôi. –

+2

Chữ ký phương thức không sửa kích thước của mảng; người gọi có thể vượt qua bất kỳ mảng kích thước nào họ muốn. – rgettman

+0

Cách thuận tiện để gọi cái này là: doSomething (chuỗi mới [] {"a", "b"}, int mới [] {1,2}). Doable. Lợi ích ở đây là trong trường hợp int, không có int int và không được tự động đóng hộp thành Integer như trong biến thể sử dụng asList(). Tuy nhiên, cá nhân tôi thích biến thể asList() ít viết hơn. – rec

4

Chỉ có một vararg được cho phép. Điều này là do nhiều đối số vararg không rõ ràng. Ví dụ, nếu bạn vượt qua trong hai varargs của cùng một lớp?

public void doSomething(String...args1, String...args2); 

Trường hợp nào args1 kết thúc và args2 bắt đầu? Hoặc làm thế nào về một cái gì đó khó hiểu hơn ở đây.

class SuperClass{} 
class ChildClass extends SuperClass{} 
public void doSomething(SuperClass...args1, ChildClass...args2); 

ChildClass mở rộng SuperClass và có thể tồn tại hợp pháp trong args1 hoặc args2. Sự nhầm lẫn này là lý do tại sao chỉ cho phép varargs.

varargs cũng phải xuất hiện ở cuối khai báo phương pháp.

Chỉ cần khai báo loại cụ thể thay vì 2 mảng.

+0

okay cảm ơn bạn! Nhưng tôi hiểu 2 varargs cùng loại không có ý nghĩa ^^ –

+0

@ T_01 Tôi đã có một ví dụ về lý do tại sao nó không thể với 2 loại đối số khác nhau. Trình biên dịch không đủ thông minh để xử lý điều đó, ngay cả khi các lớp không chia sẻ các loại. –

4

Mặc dù loại điều là thỉnh thoảng hữu ích, thường nếu bạn thấy rằng bạn đang đánh một hạn chế trong Java bạn có thể có thể thiết kế lại một cái gì đó và đi ra tốt hơn nhiều. Dưới đây là một số cách khác để xem xét ...

Nếu hai danh sách có liên quan, bạn có thể muốn tạo lớp bao bọc cho hai danh sách khác nhau và chuyển vào trình bao bọc. Trình bao bọc xung quanh bộ sưu tập hầu như luôn là ý tưởng hay - chúng cung cấp cho bạn một nơi để thêm mã liên quan đến bộ sưu tập.

Nếu đây là cách để khởi tạo dữ liệu, hãy phân tích dữ liệu từ chuỗi. Ví dụ: "abc, 123: def, 456: jhi, 789" gần như dễ dàng phân tách với 2 câu lệnh tách và một vòng lặp (2-3 dòng mã). Bạn thậm chí có thể tạo một lớp phân tích cú pháp tùy chỉnh nhỏ phân tích một chuỗi như vậy thành một cấu trúc mà bạn đưa vào phương thức của mình.

Hmm - thành thật hỗ trợ từ việc khởi tạo dữ liệu Tôi thậm chí không biết tại sao bạn vẫn muốn làm điều này, bất kỳ trường hợp nào khác và tôi mong bạn sẽ chuyển 2 bộ sưu tập đó ở tất cả.

7

Một thiết kế API có thể trong đó mã gọi trông giống như

doSomething("a", "b").with(1,2); 

thông qua API "thông thạo"

public Intermediary doSomething(String... strings) 
{ 
    return new Intermediary(strings); 
} 

class Intermediary 
{ 
    ... 
    public void with(int... ints) 
    { 
     reallyDoSomething(strings, ints); 
    } 
} 

void reallyDoSomething(String[] strings, int[] ints) 
{ 
    ... 
} 

Điều nguy hiểm là nếu các lập trình viên quên gọi with(...)

doSomething("a", "b"); // nothing is done 

Có thể đây là một chút tốt hơn

with("a", "b").and(1, 2).doSomething(); 
1

Đây là một chủ đề cũ, nhưng tôi nghĩ điều này sẽ hữu ích bất kể.

Giải pháp tôi tìm thấy không phải là rất gọn gàng nhưng nó hoạt động. Tôi đã tạo ra một lớp riêng để xử lý việc nâng hạng nặng. Nó chỉ có hai biến tôi cần và getters của chúng. Hàm khởi tạo xử lý các phương thức thiết lập của riêng nó.

Tôi cần chuyển đối tượng hướng và đối tượng Dữ liệu tương ứng. Điều này cũng giải quyết vấn đề có thể xảy ra của các cặp dữ liệu không đồng đều, nhưng điều đó có lẽ chỉ dành cho nhu cầu sử dụng của tôi.

public class DataDirectionPair{ 

    Data dat; 
    Directions dir; 

    public DataDirectionPair(Data dat, Directions dir) { 
     super(); 
     this.dat = dat; 
     this.dir = dir; 
    } 

    /** 
    * @return the node 
    */ 
    public Node getNode() { 
     return node; 
    } 

    /** 
    * @return the direction 
    */ 
    public Directions getDir() { 
     return dir; 
    } 
} 

tôi sẽ sau đó chỉ cần vượt qua lớp này như vararg cho phương pháp

public void method(DataDirectionPair... ndPair){ 
    for(DataDirectionPair temp : ndPair){ 
     this.node = temp.getNode(); 
     this.direction = temp.getDir(); 
     //or use it however you want 
    } 
} 
2

Bạn có thể làm một cái gì đó như thế này, sau đó bạn có thể cast và thêm logic thêm bên trong phương thức đó.

public void doSomething(Object... stringOrIntValues) { 
    ... 
    ... 
} 

Và sử dụng phương pháp này như sau:

doSomething(stringValue1, stringValue2, intValue1, intValue2,   
    intValue3); 
0

Tôi chỉ cần đọc một câu hỏi về vấn đề này "mẫu", nhưng nó đã được gỡ bỏ, vì vậy tôi muốn đề xuất một cách tiếp cận khác nhau cho vấn đề này , như tôi đã không thấy ở đây giải pháp này. Thay vào đó, để buộc nhà phát triển gói thông số đầu vào vào Danh sách hoặc Mảng, sẽ hữu ích khi sử dụng phương pháp "cà ri" hoặc mô hình trình xây dựng tốt hơn.

Xét đoạn mã sau:

/** 
* Just a trivial implementation 
*/ 
public class JavaWithCurry { 

    private List<Integer> numbers = new ArrayList<Integer>(); 
    private List<String> strings = new ArrayList<String>(); 

    public JavaWithCurry doSomething(int n) { 

     numbers.add(n); 

     return this; 
    } 

    public JavaWithCurry doSomething(String s) { 

     strings.add(s); 

     return this; 

    } 

    public void result() { 

     int sum = -1; 

     for (int n : numbers) { 
      sum += n; 
     } 


     StringBuilder out = new StringBuilder(); 

     for (String s : strings) { 

      out.append(s).append(" "); 

     } 

     System.out.println(out.toString() + sum); 

    } 

    public static void main(String[] args) { 

     JavaWithCurry jwc = new JavaWithCurry(); 

     jwc.doSomething(1) 
       .doSomething(2) 
       .doSomething(3) 
       .doSomething(4) 
       .doSomething(5) 
       .doSomething("a") 
       .doSomething("b") 
       .doSomething("c") 
       .result(); 

    } 

} 

Như bạn có thể nhìn thấy bạn trong cách này, bạn có thể thêm các yếu tố mới trong đó gõ bạn cần khi bạn cần.

Tất cả việc triển khai được thực hiện.

0

Nếu bạn không định chuyển một số lượng lớn các chuỗi cho đối số đầu tiên, bạn có thể cung cấp một loạt các quá tải có số lượng chuỗi khác nhau và bọc chúng trong một mảng trước khi gọi phương thức mảng là đối số đầu tiên.

public void doSomething(int... i){ 
    doSomething(new String[0], i); 
} 
public void doSomething(String s, int... i){ 
    doSomething(new String[]{ s }, i); 
} 
public void doSomething(String s1, String s2, int... i){ 
    doSomething(new String[]{ s1, s2 }, i); 
} 
public void doSomething(String s1, String s2, String s3, int... i){ 
    doSomething(new String[]{ s1, s2, s3 }, i); 
} 
public void doSomething(String[] s, int... i) { 
    // ... 
    // ... 
} 
0

follwing trên Lemuel Adane (cant bình luận về bài viết, do thiếu đại diện :))

nếu bạn sử dụng

public void f(Object... args){} 

sau đó bạn có thể sử dụng vòng lặp How to determine an object's class (in Java)?

ví dụ:

{ 
    int i = 0; 
    while(i< args.length && args[i] instanceof String){ 
     System.out.println((String) args[i]); 
     i++ ; 
    } 
    int sum = 0; 
    while(i< args.length){ 
     sum += (int) args[i]; 
     i++ ; 
    } 
    System.out.println(sum); 
} 

hoặc bất kỳ điều gì bạn định làm.

0

Bạn có thể chuyển đổi varargs của bạn để các mảng

public void doSomething(String[] s, int[] i) { 
    ... 
} 

sau đó với một số phương pháp helper để chuyển đổi varargs của bạn để mảng như thế này:

public static int[] intsAsArray(int... ints) { 
    return ints; 
} 

public static <T> T[] asArray(T... ts) { 
    return ts; 
} 

Sau đó, bạn có thể sử dụng những phương pháp helper để chuyển đổi vararged của bạn thông số.

doSomething(asArray("a", "b", "c", "d"), intsAsArray(1, 2, 3)); 
2

Không thể vì kỹ thuật Java Ngôn ngữ nói như vậy (xem 8.4.1. Formal Parameters):

Tham số chính thức cuối cùng của một phương pháp hoặc constructor là đặc biệt: nó có thể là một tham số arity biến , được biểu thị bằng dấu ba chấm theo loại.

Lưu ý rằng dấu ba chấm (...) là mã thông báo cho chính nó (§3.11). Có thể đặt khoảng trống giữa nó và loại, nhưng đây là không được khuyến khích như một vấn đề về phong cách.

Nếu tham số chính thức cuối cùng là thông số biến số biến thiên, phương pháp là phương pháp xác định biến số. Nếu không, đó là phương thức tinh khiết cố định .

Là tại sao chỉ có một và chỉ tham số cuối cùng, đó sẽ là một đoán, nhưng có lẽ vì cho phép có thể dẫn đến các vấn đề undecidable hoặc mơ hồ (ví dụ như xem xét những gì xảy ra với method(String... strings, Object... objects)), và chỉ cho phép không giao nhau loại sẽ dẫn đến các biến chứng (ví dụ như xem xét tái cấu trúc mà trước đây không giao nhau loại đột nhiên), thiếu rõ ràng khi nó không hoặc không làm việc, và phức tạp cho trình biên dịch để quyết định khi nó được áp dụng hay không.

+0

tôi tự hỏi tại sao ai đó woud trả lời trên một câu hỏi 4 tuổi, mà đã có một câu trả lời chấp nhận .. ^^ nhưng cảm ơn bạn anyway –

+0

@ T_01 tôi đã viết này như một câu trả lời cho câu hỏi khác khi nó đã được đóng cửa như là một bản sao của câu hỏi này, vì vậy tôi quyết định không để điều đó lãng phí và đăng nó ở đây thay vào đó. –

Các vấn đề liên quan