Làm thế nào để trả về nhiều đối tượng từ một phương thức Java?


172

Tôi muốn trả về hai đối tượng từ một phương thức Java và đang tự hỏi điều gì có thể là một cách tốt để làm như vậy?

Những cách có thể, tôi có thể nghĩ đến là: trở lại một HashMap(kể từ khi hai đối tượng có liên quan) hoặc trả lại một ArrayListsố Objectđối tượng.

Nói chính xác hơn, hai đối tượng tôi muốn trả về là (a) Listđối tượng và (b) tên được phân tách bằng dấu phẩy của cùng một tên.

Tôi muốn trả về hai Đối tượng này từ một phương thức vì tôi không muốn lặp qua danh sách các đối tượng để lấy các tên được phân tách bằng dấu phẩy (mà tôi có thể thực hiện trong cùng một vòng lặp trong phương thức này).

Bằng cách nào đó, trả lại HashMapkhông phải là một cách rất thanh lịch để làm như vậy.


1
Danh sách và CSV về cơ bản là các chế độ xem khác nhau của cùng một dữ liệu? Nghe có vẻ như những gì bạn cần là một Đối tượng nơi bạn có một Listtham chiếu duy nhất và một loại bảng tra cứu.
James P.

Câu trả lời:


128

Nếu bạn muốn trả về hai đối tượng, bạn thường muốn trả về một đối tượng duy nhất đóng gói hai đối tượng thay thế.

Bạn có thể trả về Danh sách các NamedObjectđối tượng như thế này:

public class NamedObject<T> {
  public final String name;
  public final T object;

  public NamedObject(String name, T object) {
    this.name = name;
    this.object = object;
  }
}

Sau đó, bạn có thể dễ dàng trở lại a List<NamedObject<WhateverTypeYouWant>>.

Ngoài ra: Tại sao bạn muốn trả về danh sách tên được phân tách bằng dấu phẩy thay vì a List<String>? Hoặc tốt hơn nữa, trả về a Map<String,TheObjectType>với các khóa là tên và giá trị của các đối tượng (trừ khi các đối tượng của bạn có thứ tự được chỉ định, trong trường hợp đó NavigableMapcó thể là những gì bạn muốn.


Lý do trả về danh sách được phân tách bằng dấu phẩy là: Nếu tôi không tạo danh sách ở đây, tôi sẽ phải thực hiện việc này trong trình gọi bằng cách lặp qua các đối tượng (cần có giá trị CS). Có lẽ, tôi đang tối ưu hóa trước một cách không cần thiết.
Jagmal

2
Tôi đã luôn tự hỏi tại sao Java không có lớp Pair <T, U> vì lý do này.
David Koelle

Jagmal: có, nếu rason duy nhất trả về danh sách được phân tách bằng dấu phẩy là tối ưu hóa này, thì hãy quên nó đi.
Joachim Sauer

Điều này chỉ hoạt động tốt nếu các mục bạn muốn trả lại cùng loại hoặc ít nhất có tổ tiên chung gần gũi. Ý tôi là, sử dụng Object thay cho AnyTypeYouWant không gọn gàng lắm.
David Hanak

@David: Tôi đồng ý rằng việc sử dụng Object ở đây không phải là rất gọn gàng, nhưng sau đó một lần nữa trả lại các đối tượng mà không có bất kỳ tổ tiên chung nào (ngoại trừ Object dĩ ​​nhiên) cũng không gọn gàng lắm. Tôi thậm chí sẽ nói đó là mùi mã, nếu bạn cần.
Joachim Sauer

69

Nếu bạn biết bạn sẽ trả về hai đối tượng, bạn cũng có thể sử dụng một cặp chung:

public class Pair<A,B> {
    public final A a;
    public final B b;

    public Pair(A a, B b) {
        this.a = a;
        this.b = b;
    }
};

Chỉnh sửa Một thực hiện đầy đủ hơn hình thành ở trên:

package util;

public class Pair<A,B> {

    public static <P, Q> Pair<P, Q> makePair(P p, Q q) {
        return new Pair<P, Q>(p, q);
    }

    public final A a;
    public final B b;

    public Pair(A a, B b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((a == null) ? 0 : a.hashCode());
        result = prime * result + ((b == null) ? 0 : b.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        Pair other = (Pair) obj;
        if (a == null) {
            if (other.a != null) {
                return false;
            }
        } else if (!a.equals(other.a)) {
            return false;
        }
        if (b == null) {
            if (other.b != null) {
                return false;
            }
        } else if (!b.equals(other.b)) {
            return false;
        }
        return true;
    }

    public boolean isInstance(Class<?> classA, Class<?> classB) {
        return classA.isInstance(a) && classB.isInstance(b);
    }

    @SuppressWarnings("unchecked")
    public static <P, Q> Pair<P, Q> cast(Pair<?, ?> pair, Class<P> pClass, Class<Q> qClass) {

        if (pair.isInstance(pClass, qClass)) {
            return (Pair<P, Q>) pair;
        }

        throw new ClassCastException();

    }

}

Lưu ý, chủ yếu xoay quanh sự mộc mạc với Java & generic:

  • cả ab là bất biến.
  • makePairPhương thức tĩnh giúp bạn với việc gõ tấm nồi hơi, mà toán tử kim cương trong Java 7 sẽ làm giảm bớt sự khó chịu. Có một số công việc để làm cho điều này thực sự tốt đẹp: thuốc generic, nhưng bây giờ nó sẽ ổn thôi. (cf PECS)
  • hashcodeequals được tạo ra bởi nhật thực.
  • thời gian biên dịch đúc trong cast phương thức là ok, nhưng có vẻ không đúng lắm.
  • Tôi không chắc chắn nếu các ký tự đại diện trong isInstance là cần thiết.
  • Tôi vừa mới viết bài này để phản hồi các bình luận, chỉ nhằm mục đích minh họa.

Tôi thường có lớp này gõ trong mỗi cơ sở mã mà tôi làm việc. Tôi cũng muốn thêm: một hàm hashCode / bằng, và có thể là một phương thức isInstance () và cast () tĩnh.
jamesh

Chắc chắn, có nhiều cách để làm cho lớp này thông minh hơn và thuận tiện hơn để sử dụng. Phiên bản trên bao gồm những gì vừa đủ trong tuyên bố một lần.
David Hanak

@jamesh: bạn có phiền khi viết Cặp của bạn một cách chi tiết đầy đủ ở đây không? Tôi muốn biết nó trông như thế nào sau khi cung cấp "một hàm thực hiện hashCode / bằng và có thể là một phương thức isInstance () và cast () tĩnh". Cảm ơn bạn.
Qiang Li

@QiangLi - Tôi thường tạo các phương thức hashcode & bằng. Phương thức sơ thẩm isInstance có hai lớp và đảm bảo rằng a & b của thể hiện là các thể hiện của các lớp đó. Cast lấy một cặp <?,?> Và cẩn thận chuyển nó thành một cặp <A, B>. Việc triển khai nên khá dễ dàng (gợi ý: Class.cast (), Class.isInstance ())
jamesh

2
Đó là một Pairthực hiện rất tốt đẹp . Một thay đổi nhỏ tôi sẽ thực hiện: thêm một tin nhắn vào ClassCastException. Nếu không, gỡ lỗi trở thành một cơn ác mộng nếu điều này không thành công vì một số lý do. (và rawtypes suppress-cảnh báo sẽ không cần thiết nếu bạn muốn cast vào Pair<?,?>(mà làm việc, vì bạn chỉ cần Objectphương pháp từ ab) Bạn có phiền nếu tôi tinh chỉnh mã.?
Joachim Sauer

25

Trong trường hợp phương thức bạn gọi là riêng tư hoặc được gọi từ một địa điểm, hãy thử

return new Object[]{value1, value2};

Người gọi trông giống như:

Object[] temp=myMethod(parameters);
Type1 value1=(Type1)temp[0];  //For code clarity: temp[0] is not descriptive
Type2 value2=(Type2)temp[1];

Ví dụ về cặp của David Hanak không có lợi ích cú pháp và bị giới hạn ở hai giá trị.

return new Pair<Type1,Type2>(value1, value2);

Và người gọi trông giống như:

Pair<Type1, Type2> temp=myMethod(parameters);
Type1 value1=temp.a;  //For code clarity: temp.a is not descriptive
Type2 value2=temp.b;

7
Cặp có lợi ích kiểm soát loại lớp
Hlex

5
IMHO, đừng đi theo cách này - tuyên bố nói quá ít về các giá trị trả về dự kiến. AFAIK, nó được ưu tiên rộng rãi hơn để tạo các lớp chung xác định số lượng tham số được trả về và loại của các tham số đó. Pair<T1, T2>, Tuple<T1, T2, T3>, Tuple<T1, T2, T3, T4>, Vv Sau đó, một cụ thể các chương trình sử dụng số lượng và loại của các tham số Pair<int, String> temp = ...hoặc bất cứ điều gì.
ToolmakerSteve

22

Bạn có thể sử dụng bất kỳ cách nào sau đây:

private static final int RETURN_COUNT = 2;
private static final int VALUE_A = 0;
private static final int VALUE_B = 1;
private static final String A = "a";
private static final String B = "b";

1) Sử dụng mảng

private static String[] methodWithArrayResult() {
    //...
    return new String[]{"valueA", "valueB"};
}

private static void usingArrayResultTest() {
    String[] result = methodWithArrayResult();
    System.out.println();
    System.out.println("A = " + result[VALUE_A]);
    System.out.println("B = " + result[VALUE_B]);
}

2) Sử dụng ArrayList

private static List<String> methodWithListResult() {
    //...
    return Arrays.asList("valueA", "valueB");
}

private static void usingListResultTest() {
    List<String> result = methodWithListResult();
    System.out.println();
    System.out.println("A = " + result.get(VALUE_A));
    System.out.println("B = " + result.get(VALUE_B));
}

3) Sử dụng HashMap

private static Map<String, String> methodWithMapResult() {
    Map<String, String> result = new HashMap<>(RETURN_COUNT);
    result.put(A, "valueA");
    result.put(B, "valueB");
    //...
    return result;
}

private static void usingMapResultTest() {
    Map<String, String> result = methodWithMapResult();
    System.out.println();
    System.out.println("A = " + result.get(A));
    System.out.println("B = " + result.get(B));
}

4) Sử dụng lớp container tùy chỉnh của bạn

private static class MyContainer<M,N> {
    private final M first;
    private final N second;

    public MyContainer(M first, N second) {
        this.first = first;
        this.second = second;
    }

    public M getFirst() {
        return first;
    }

    public N getSecond() {
        return second;
    }

    // + hashcode, equals, toString if need
}

private static MyContainer<String, String> methodWithContainerResult() {
    //...
    return new MyContainer("valueA", "valueB");
}

private static void usingContainerResultTest() {
    MyContainer<String, String> result = methodWithContainerResult();
    System.out.println();
    System.out.println("A = " + result.getFirst());
    System.out.println("B = " + result.getSecond());
}

5) Sử dụng AbstractMap.simpleEntry

private static AbstractMap.SimpleEntry<String, String> methodWithAbstractMapSimpleEntryResult() {
    //...
    return new AbstractMap.SimpleEntry<>("valueA", "valueB");
}

private static void usingAbstractMapSimpleResultTest() {
    AbstractMap.SimpleEntry<String, String> result = methodWithAbstractMapSimpleEntryResult();
    System.out.println();
    System.out.println("A = " + result.getKey());
    System.out.println("B = " + result.getValue());
}

6) Sử dụng Pair của Apache Commons

private static Pair<String, String> methodWithPairResult() {
    //...
    return new ImmutablePair<>("valueA", "valueB");
}

private static void usingPairResultTest() {
    Pair<String, String> result = methodWithPairResult();
    System.out.println();
    System.out.println("A = " + result.getKey());
    System.out.println("B = " + result.getValue());
}

16

Tôi hầu như luôn luôn kết thúc việc xác định các lớp n-Tuple khi tôi viết mã bằng Java. Ví dụ:

public class Tuple2<T1,T2> {
  private T1 f1;
  private T2 f2;
  public Tuple2(T1 f1, T2 f2) {
    this.f1 = f1; this.f2 = f2;
  }
  public T1 getF1() {return f1;}
  public T2 getF2() {return f2;}
}

Tôi biết nó hơi xấu, nhưng nó hoạt động và bạn chỉ cần xác định các loại tuple của mình một lần. Tuples là thứ mà Java thực sự thiếu.

EDIT: Ví dụ của David Hanak thanh lịch hơn, vì nó tránh việc xác định getters và vẫn giữ cho đối tượng không thay đổi.


9

Trước Java 5, tôi đồng ý rằng giải pháp Bản đồ không lý tưởng. Nó sẽ không cung cấp cho bạn biên dịch kiểm tra loại thời gian để có thể gây ra sự cố khi chạy. Tuy nhiên, với Java 5, chúng ta có Loại chung.

Vì vậy, phương pháp của bạn có thể trông như thế này:

public Map<String, MyType> doStuff();

MyType tất nhiên là loại đối tượng bạn đang trả về.

Về cơ bản tôi nghĩ rằng việc trả lại Bản đồ là giải pháp phù hợp trong trường hợp này bởi vì đó chính xác là những gì bạn muốn trả về - ánh xạ của một chuỗi tới một đối tượng.


Điều này sẽ không hoạt động nếu bất kỳ tên nào va chạm. Một danh sách có thể chứa các bản sao, nhưng Bản đồ không thể (chứa các khóa trùng lặp).
tvanfosson

Tất nhiên. Tôi đã đưa ra các giả định dựa trên câu hỏi - có lẽ chắc chắn :)
kipz

Mặc dù giả định của bạn đúng trong trường hợp này, tôi đang đi vào lĩnh vực tối ưu hóa sớm (điều mà tôi không nên làm).
Jagmal

6

Ngoài ra, trong các tình huống tôi muốn trả về một số thứ từ một phương thức, đôi khi tôi sẽ sử dụng cơ chế gọi lại thay vì một container. Điều này hoạt động rất tốt trong các tình huống mà tôi không thể chỉ định trước được bao nhiêu đối tượng sẽ được tạo.

Với vấn đề cụ thể của bạn, nó sẽ trông giống như thế này:

public class ResultsConsumer implements ResultsGenerator.ResultsCallback
{
    public void handleResult( String name, Object value )
    {
        ... 
    }
}

public class ResultsGenerator
{
    public interface ResultsCallback
    {
        void handleResult( String aName, Object aValue );
    }

    public void generateResults( ResultsGenerator.ResultsCallback aCallback )
    {
        Object value = null;
        String name = null;

        ...

        aCallback.handleResult( name, value );
    }
}

xin lỗi vì đã nhận xét về câu trả lời rất cũ của bạn, nhưng làm thế nào để gọi lại liên quan đến bộ sưu tập Rác? Tôi chắc chắn không hiểu rõ về quản lý bộ nhớ java, nếu bạn có đối tượng Agọi đối tượng B.getResult()B.getResult()gọi A.finishResult()như một callback, thì đối tượng Bcó được thu gom rác hay nó ở lại cho đến khi A kết thúc ?? có lẽ là một câu hỏi ngu ngốc nhưng tôi có một sự nhầm lẫn cơ bản!
dây00

6

Apache Commons đã tăng gấp ba và gấp ba cho việc này:

  • ImmutablePair<L,R> Một cặp bất biến bao gồm hai yếu tố Object.
  • ImmutableTriple<L,M,R> Một bộ ba bất biến bao gồm ba yếu tố Object.
  • MutablePair<L,R> Một cặp đột biến bao gồm hai yếu tố Object.
  • MutableTriple<L,M,R> Một bộ ba đột biến bao gồm ba yếu tố Object.
  • Pair<L,R> Một cặp bao gồm hai yếu tố.
  • Triple<L,M,R> Một bộ ba bao gồm ba yếu tố.

Nguồn: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html


6

Trong trường hợp của bạn, bình luận có thể là một cách tốt để đi, trong Android, bạn có thể sử dụng Pair . Đơn giản

return new Pair<>(yourList, yourCommaSeparatedValues);

5

Sử dụng các đối tượng Entry sau Ví dụ:

public Entry<A,B> methodname(arg)
{
.......

return new AbstractMap.simpleEntry<A,B>(instanceOfA,instanceOfB);
}

5

Về vấn đề về nhiều giá trị trả về nói chung, tôi thường sử dụng một lớp trình trợ giúp nhỏ bao bọc một giá trị trả về duy nhất và được truyền dưới dạng tham số cho phương thức:

public class ReturnParameter<T> {
    private T value;

    public ReturnParameter() { this.value = null; }
    public ReturnParameter(T initialValue) { this.value = initialValue; }

    public void set(T value) { this.value = value; }
    public T get() { return this.value; }
}

(đối với kiểu dữ liệu nguyên thủy, tôi sử dụng các biến thể nhỏ để lưu trữ trực tiếp giá trị)

Một phương thức muốn trả về nhiều giá trị sau đó sẽ được khai báo như sau:

public void methodThatReturnsTwoValues(ReturnParameter<ClassA> nameForFirstValueToReturn, ReturnParameter<ClassB> nameForSecondValueToReturn) {
    //...
    nameForFirstValueToReturn.set("...");
    nameForSecondValueToReturn.set("...");
    //...
}

Có lẽ nhược điểm lớn là người gọi phải chuẩn bị trước các đối tượng trả về trong trường hợp anh ta muốn sử dụng chúng (và phương thức nên kiểm tra các con trỏ null)

ReturnParameter<ClassA> nameForFirstValue = new ReturnParameter<ClassA>();
ReturnParameter<ClassB> nameForSecondValue = new ReturnParameter<ClassB>();
methodThatReturnsTwoValues(nameForFirstValue, nameForSecondValue);

Ưu điểm (so với các giải pháp khác được đề xuất):

  • Bạn không phải tạo một khai báo lớp đặc biệt cho các phương thức riêng lẻ và các kiểu trả về của nó
  • Các tham số có được một tên và do đó dễ phân biệt hơn khi nhìn vào chữ ký phương thức
  • Nhập an toàn cho từng tham số

Cảm ơn về một giải pháp cung cấp tên và loại an toàn cho từng giá trị được trả về, mà không yêu cầu khai báo lớp cho mỗi bộ loại giá trị được trả về.
ToolmakerSteve

3

Tất cả các giải pháp có thể sẽ là một loại bùn (như các đối tượng chứa, ý tưởng HashMap của bạn, nhiều giá trị trả về của Hồi giáo như được thực hiện thông qua các mảng). Tôi khuyên bạn nên tạo lại danh sách được phân tách bằng dấu phẩy từ Danh sách được trả về. Mã sẽ kết thúc sạch hơn rất nhiều.


Tôi đồng ý với bạn về điều này nhưng nếu tôi làm như vậy, tôi sẽ kết thúc vòng lặp hai lần (tôi thực sự đang tạo ra các yếu tố của danh sách từng cái một trong phương thức hiện có).
Jagmal

1
@Jagmal: bạn có thể lặp lại hai lần, nhưng điều đó không quan trọng trong hầu hết thời gian (xem câu trả lời của gizmos).
Joachim Sauer

1
Vâng, đừng cố tối ưu hóa mã của bạn trừ khi bạn thực sự phải làm vậy. gizmo rất đúng về điều đó.
Bombe

3

Giữ cho nó đơn giản và tạo một lớp cho tình huống nhiều kết quả. Ví dụ này chấp nhận một ArrayList và một văn bản thông báo từ một cơ sở dữ liệu getInfo.

Nơi bạn gọi thủ tục trả về nhiều giá trị mà bạn mã:

multResult res = mydb.getInfo(); 

Trong thường trình getInfo, bạn mã:

ArrayList<String> list= new ArrayList<String>();
add values to the list...
return new multResult("the message", list);

và định nghĩa một lớp đa lớp với:

public class multResult {
    public String message; // or create a getter if you don't like public
    public ArrayList<String> list;
    multResult(String m, ArrayList<String> l){
        message = m;
        list= l;
}

}


2

Như tôi thấy nó thực sự có ba lựa chọn ở đây và giải pháp phụ thuộc vào bối cảnh. Bạn có thể chọn để thực hiện việc xây dựng tên trong phương thức tạo danh sách. Đây là lựa chọn bạn đã chọn, nhưng tôi không nghĩ đó là lựa chọn tốt nhất. Bạn đang tạo một khớp nối trong phương thức sản xuất với phương thức tiêu thụ không cần tồn tại. Những người gọi khác có thể không cần thêm thông tin và bạn sẽ tính thêm thông tin cho những người gọi này.

Ngoài ra, bạn có thể có phương thức gọi tính toán tên. Nếu chỉ có một người gọi cần thông tin này, bạn có thể dừng ở đó. Bạn không có phụ thuộc thêm và trong khi có một chút tính toán liên quan, bạn đã tránh làm cho phương pháp xây dựng của mình quá cụ thể. Đây là một sự đánh đổi tốt.

Cuối cùng, bạn có thể có danh sách chịu trách nhiệm tạo tên. Đây là con đường tôi sẽ đi nếu việc tính toán cần phải được thực hiện bởi nhiều người gọi. Tôi nghĩ rằng điều này đặt trách nhiệm cho việc tạo ra các tên với lớp có liên quan chặt chẽ nhất với chính các đối tượng.

Trong trường hợp sau, giải pháp của tôi sẽ là tạo một lớp List chuyên biệt trả về một chuỗi được phân tách bằng dấu phẩy của tên của các đối tượng mà nó chứa. Làm cho lớp đủ thông minh để nó xây dựng chuỗi tên khi đang di chuyển khi các đối tượng được thêm và xóa khỏi nó. Sau đó trả về một thể hiện của danh sách này và gọi phương thức tạo tên nếu cần. Mặc dù nó có thể gần như hiệu quả (và đơn giản hơn) chỉ đơn giản là trì hoãn việc tính toán tên cho đến khi lần đầu tiên phương thức được gọi và lưu trữ sau đó (lười tải). Nếu bạn thêm / xóa một đối tượng, bạn chỉ cần xóa giá trị đã tính và để nó được tính toán lại trong cuộc gọi tiếp theo.


2

Có thể làm một số thứ như một tuple trong ngôn ngữ động (Python)

public class Tuple {
private Object[] multiReturns;

private Tuple(Object... multiReturns) {
    this.multiReturns = multiReturns;
}

public static Tuple _t(Object... multiReturns){
    return new Tuple(multiReturns);
}

public <T> T at(int index, Class<T> someClass) {
    return someClass.cast(multiReturns[index]);
}
}

và sử dụng như thế này

public Tuple returnMultiValues(){
   return Tuple._t(new ArrayList(),new HashMap())
}


Tuple t = returnMultiValues();
ArrayList list = t.at(0,ArrayList.class);

2

Tôi đã làm theo một cách tiếp cận tương tự như mô tả trong các câu trả lời khác với một vài điều chỉnh dựa trên yêu cầu tôi có, về cơ bản tôi đã tạo các lớp sau (Chỉ trong trường hợp, mọi thứ đều là Java):

public class Pair<L, R> {
    final L left;
    final R right;

    public Pair(L left, R right) {
        this.left = left;
        this.right = right;
    }

    public <T> T get(Class<T> param) {
        return (T) (param == this.left.getClass() ? this.left : this.right);
    }

    public static <L, R> Pair<L, R> of(L left, R right) {
        return new Pair<L, R>(left, right);
    }
}

Sau đó, yêu cầu của tôi rất đơn giản, trong Lớp kho lưu trữ đến DB, đối với Phương thức lấy hơn là lấy dữ liệu từ DB, tôi cần kiểm tra xem nó có thất bại hay thành công không, sau đó, nếu thành công, tôi cần chơi với danh sách trả về , nếu thất bại, dừng thực thi và thông báo lỗi.

Vì vậy, ví dụ, các phương thức của tôi là như thế này:

public Pair<ResultMessage, List<Customer>> getCustomers() {
    List<Customer> list = new ArrayList<Customer>();
    try {
    /*
    * Do some work to get the list of Customers from the DB
    * */
    } catch (SQLException e) {
        return Pair.of(
                       new ResultMessage(e.getErrorCode(), e.getMessage()), // Left 
                       null);  // Right
    }
    return Pair.of(
                   new ResultMessage(0, "SUCCESS"), // Left 
                   list); // Right
}

Trong đó resultMessage chỉ là một lớp có hai trường (mã / thông báo) và Khách hàng là bất kỳ lớp nào có một loạt các trường đến từ DB.

Sau đó, để kiểm tra kết quả tôi chỉ cần làm điều này:

void doSomething(){
    Pair<ResultMessage, List<Customer>> customerResult = _repository.getCustomers();
    if (customerResult.get(ResultMessage.class).getCode() == 0) {
        List<Customer> listOfCustomers = customerResult.get(List.class);
        System.out.println("do SOMETHING with the list ;) ");
    }else {
        System.out.println("Raised Error... do nothing!");
    }
}

1

Trong C ++ (STL) có một lớp cặp để bó hai đối tượng. Trong Java Generics, một lớp cặp không có sẵn, mặc dù có một số nhu cầu cho nó. Bạn có thể dễ dàng thực hiện nó mặc dù.

Tuy nhiên, tôi đồng ý với một số câu trả lời khác rằng nếu bạn cần trả về hai hoặc nhiều đối tượng từ một phương thức, sẽ tốt hơn nếu gói chúng trong một lớp.


1

Tại sao không tạo một WhateverFunctionResultđối tượng chứa kết quả của bạn logic cần thiết để phân tích các kết quả này, lặp đi lặp lại sau đó, v.v.

  1. Các đối tượng kết quả này được liên kết chặt chẽ với nhau / liên quan và thuộc về nhau, hoặc:
  2. chúng không liên quan với nhau, trong trường hợp đó chức năng của bạn không được xác định rõ về những gì nó đang cố gắng thực hiện (nghĩa là thực hiện hai việc khác nhau)

Tôi thấy loại vấn đề này lặp đi lặp lại. Đừng ngại tạo các lớp chứa / kết quả của riêng bạn có chứa dữ liệu và chức năng liên quan để xử lý việc này. Nếu bạn chỉ đơn giản chuyển những thứ xung quanh trong một HashMaphoặc tương tự, thì khách hàng của bạn phải kéo bản đồ này ra và dò tìm nội dung mỗi lần họ muốn sử dụng kết quả.


Bởi vì Pita phải xác định một lớp bất cứ khi nào bạn cần trả về nhiều giá trị, chỉ vì ngôn ngữ thiếu tính năng thường hữu ích này;) Nhưng nghiêm túc, những gì bạn đề xuất thường rất đáng làm.
ToolmakerSteve

1
public class MultipleReturnValues {

    public MultipleReturnValues() {
    }

    public static void functionWithSeveralReturnValues(final String[] returnValues) {
        returnValues[0] = "return value 1";
        returnValues[1] = "return value 2";
    }

    public static void main(String[] args) {
        String[] returnValues = new String[2];
        functionWithSeveralReturnValues(returnValues);
        System.out.println("returnValues[0] = " + returnValues[0]);
        System.out.println("returnValues[1] = " + returnValues[1]);
    }

}

1

Đây không phải là trả lời chính xác câu hỏi, nhưng vì mọi giải pháp được đưa ra ở đây đều có một số nhược điểm, tôi khuyên bạn nên thử cấu trúc lại mã của mình một chút để bạn chỉ cần trả về một giá trị.

Trường hợp một.

Bạn cần một cái gì đó bên trong cũng như bên ngoài phương pháp của bạn. Tại sao không tính toán nó ra bên ngoài và truyền nó cho phương thức?

Thay vì:

[thingA, thingB] = createThings(...);  // just a conceptual syntax of method returning two values, not valid in Java

Thử:

thingA = createThingA(...);
thingB = createThingB(thingA, ...);

Điều này sẽ đáp ứng hầu hết các nhu cầu của bạn, vì trong hầu hết các tình huống, một giá trị được tạo trước giá trị kia và bạn có thể phân chia việc tạo chúng theo hai phương thức. Hạn chế là phương thức đó createThingsBcó một tham số bổ sung so với createThingsvà có thể bạn đang truyền chính xác cùng một danh sách các tham số hai lần cho các phương thức khác nhau.


Trường hợp hai.

Giải pháp rõ ràng nhất từ ​​trước đến nay và một phiên bản đơn giản hóa của trường hợp một. Không phải lúc nào cũng có thể, nhưng có lẽ cả hai giá trị có thể được tạo độc lập với nhau?

Thay vì:

[thingA, thingB] = createThings(...);  // see above

Thử:

thingA = createThingA(...);
thingB = createThingB(...);

Để làm cho nó hữu ích hơn, hai phương thức này có thể chia sẻ một số logic phổ biến:

public ThingA createThingA(...) {
    doCommonThings(); // common logic
    // create thing A
}
public ThingB createThingB(...) {
    doCommonThings(); // common logic
    // create thing B
}

0

Truyền danh sách cho phương thức của bạn và điền vào đó, sau đó trả về Chuỗi có tên, như sau:

public String buildList(List<?> list) {
    list.add(1);
    list.add(2);
    list.add(3);
    return "something,something,something,dark side";
}

Sau đó gọi nó như thế này:

List<?> values = new ArrayList<?>();
String names = buildList(values);

-2

Tôi đã và đang sử dụng một cách tiếp cận rất cơ bản để đối phó với các vấn đề của nhiều lợi nhuận. Nó phục vụ mục đích, và tránh sự phức tạp.

Tôi gọi nó là dấu phân cách chuỗi phương pháp

Và nó hiệu quả vì nó thậm chí có thể trả về các giá trị của Nhiều loại ví dụ như int, double, char, chuỗi, v.v.

Theo cách tiếp cận này, chúng tôi sử dụng một chuỗi rất khó xảy ra nói chung. Chúng tôi gọi nó là một dải phân cách. Dấu phân cách này sẽ được sử dụng để phân tách các giá trị khác nhau khi được sử dụng trong một hàm

Ví dụ: chúng ta sẽ có lợi nhuận cuối cùng là (ví dụ) dấu phân tách intValue doubleValue ... Và sau đó, sử dụng chuỗi này, chúng ta sẽ lấy tất cả thông tin cần thiết, có thể là các loại khác nhau

Mã sau sẽ cho thấy hoạt động của khái niệm này

Dấu phân cách được sử dụng là ! @ # Và 3 giá trị đang được trả về intVal, doubleVal và stringVal

        public class TestMultipleReturns {

            public static String multipleVals() {

                String result = "";
                String separator = "!@#";


                int intVal = 5;
                // Code to process intVal

                double doubleVal = 3.14;
                // Code to process doubleVal

                String stringVal = "hello";
                // Code to process Int intVal

                result = intVal + separator + doubleVal + separator + stringVal + separator;
                return (result);
            }

            public static void main(String[] args) {

                String res = multipleVals();

                int intVal = Integer.parseInt(res.split("!@#")[0]);
                // Code to process intVal

                double doubleVal = Double.parseDouble(res.split("!@#")[1]);
                // Code to process doubleVal

                String stringVal = res.split("!@#")[2];

                System.out.println(intVal+"\n"+doubleVal+"\n"+stringVal);
            }
        }

ĐẦU RA

5
3.14
hello
BUILD SUCCESSFUL (total time: 2 seconds)

3
yuk Mùi mã rất lớn. Phân tích cú pháp, thay vì sử dụng các tính năng Hướng đối tượng có sẵn. IMO, một trong những ví dụ tồi tệ nhất về tiền mã hóa mà tôi từng thấy. Trừ khi bạn đang mô tả một tình huống mà bạn cần chuyển nhiều giá trị giữa hai chương trình độc lập hoặc giao tiếp giữa các quá trình khác và bằng cách nào đó thiếu quyền truy cập vào một cơ chế hợp lý để thực hiện điều đó (json hoặc khác).
ToolmakerSteve

-4

Trong C, bạn sẽ làm điều đó bằng cách chuyển con trỏ tới trình giữ chỗ cho kết quả dưới dạng đối số:

void getShoeAndWaistSizes(int *shoeSize, int *waistSize) {
    *shoeSize = 36;
    *waistSize = 45;
}
...
int shoeSize, waistSize;
getShoeAndWaistSize(&shoeSize, &waistSize);
int i = shoeSize + waistSize;

Hãy thử một cái gì đó tương tự, trong Java.

void getShoeAndWaistSizes(List<Integer> shoeSize, List<Integer> waistSize) {
    shoeSize.add(36);
    waistSize.add(45);
}
...
List<Integer> shoeSize = new List<>();
List<Integer> waistSize = new List<>();
getShoeAndWaistSizes(shoeSize, waistSize);
int i = shoeSize.get(0) + waistSize.get(0);

1
Tuy nhiên, trong ngôn ngữ OO, thường được coi là thích hợp hơn để thực hiện điều mà nhiều người đã đề xuất bốn năm trước câu trả lời này: nhóm hai giá trị liên quan thành một đối tượng (cặp, tuple hoặc định nghĩa lớp tùy chỉnh), sau đó có một danh sách các định nghĩa các đối tượng. Làm như vậy để tránh sự cần thiết phải vượt qua nhiều danh sách. Điều này trở nên đặc biệt quan trọng nếu cần chuyển một cặp như vậy (một yếu tố của mỗi danh sách của bạn) sang các phương thức khác, để xử lý thêm.
ToolmakerSteve

@ToolmakerSteve Để làm rõ: các danh sách được dự định có chính xác một phần tử mỗi phần tử và chỉ là một phương tiện để thực hiện một tương tự với con trỏ qua. Chúng không có ý định thu thập một số kết quả, hoặc thậm chí được sử dụng nhiều hơn một vài dòng sau khi gọi phương thức.
Adrian Panasiuk

-5

PASS A HASH VÀO PHƯƠNG PHÁP VÀ DÂN SỐ NÓ ......

public void buildResponse (Chuỗi dữ liệu, Phản hồi bản đồ);

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.