วิธีคืนวัตถุจำนวนมากจากวิธีการ Java?


172

ฉันต้องการส่งคืนวัตถุสองรายการจากวิธีการของจาวาและสงสัยว่าอะไรจะเป็นวิธีที่ดีในการทำเช่นนั้น?

วิธีการที่เป็นไปได้ที่ฉันสามารถคิดคือผลตอบแทนHashMap(ตั้งแต่วัตถุทั้งสองมีความสัมพันธ์กัน) หรือกลับArrayListของObjectวัตถุ

เพื่อให้แม่นยำยิ่งขึ้นวัตถุสองรายการที่ฉันต้องการส่งคืนคือ (a) Listของวัตถุและ (b) คั่นด้วยเครื่องหมายจุลภาคของชื่อเดียวกัน

ฉันต้องการคืนค่า Objects ทั้งสองนี้จากวิธีหนึ่งเพราะฉันไม่ต้องการวนซ้ำรายการของวัตถุเพื่อรับชื่อที่คั่นด้วยเครื่องหมายจุลภาค (ซึ่งฉันสามารถทำได้ในลูปเดียวกันในวิธีนี้)

ยังไงก็เถอะการกลับ a HashMapไม่ได้ดูเป็นวิธีที่สง่างามมากในการทำเช่นนั้น


1
รายการและ CSV แตกต่างจากมุมมองของข้อมูลเดียวกันหรือไม่ ดูเหมือนว่าสิ่งที่คุณต้องการคือวัตถุที่คุณมีการListอ้างอิงเดียวและเรียงลำดับของตารางการค้นหา
James P.

คำถามที่เกี่ยวข้อง: stackoverflow.com/questions/53453058/…
John McClane

คำตอบ:


128

หากคุณต้องการส่งคืนวัตถุสองรายการโดยปกติคุณต้องการส่งคืนวัตถุหนึ่งชิ้นที่ห่อหุ้มวัตถุสองรายการแทน

คุณสามารถส่งคืนรายการNamedObjectวัตถุเช่นนี้:

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

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

List<NamedObject<WhateverTypeYouWant>>จากนั้นคุณสามารถกลับมาเป็น

ด้วย: เหตุใดคุณจึงต้องการส่งคืนรายชื่อที่คั่นด้วยเครื่องหมายจุลภาคแทนที่จะเป็นList<String>? หรือดีกว่าให้คืนค่าMap<String,TheObjectType>ด้วยคีย์ที่เป็นชื่อและค่าของวัตถุ (ยกเว้นว่าวัตถุของคุณมีลำดับที่ระบุซึ่งในกรณี a NavigableMapอาจเป็นสิ่งที่คุณต้องการ


เหตุผลที่ส่งคืนรายการที่คั่นด้วยเครื่องหมายจุลภาคคือ: ถ้าฉันไม่สร้างรายการที่นี่ฉันจะต้องทำสิ่งนี้ในตัวเรียกโดยวนลูปผ่านวัตถุ (จำเป็นต้องมีค่า CS) บางทีฉันกำลังเพิ่มประสิทธิภาพล่วงหน้าโดยไม่จำเป็น
Jagmal

2
ฉันสงสัยอยู่เสมอว่าทำไม Java จึงไม่มีคลาส Pair <T, U> ด้วยเหตุนี้
David Koelle

Jagmal: ใช่ถ้า rason เพียงตัวเดียวที่ส่งคืนรายการที่คั่นด้วยเครื่องหมายจุลภาคคือการเพิ่มประสิทธิภาพนี้ให้ลืมมันไปเลย
Joachim Sauer

ใช้งานได้ดีหากรายการที่คุณต้องการส่งคืนเป็นคลาสเดียวกันหรืออย่างน้อยก็มีบรรพบุรุษร่วมกันอยู่ใกล้ ๆ ฉันหมายถึงการใช้ Object แทน AnythingTypeYouWant นั้นไม่ค่อยเรียบร้อย
David Hanak

@ David: ฉันยอมรับว่าการใช้ Object ที่นี่ไม่ได้เป็นระเบียบมาก แต่จากนั้นกลับมาวัตถุโดยไม่ต้องบรรพบุรุษร่วมกัน (ยกเว้น Object แน่นอน) ไม่เป็นระเบียบมากเช่นกัน ฉันยังบอกว่ามันเป็นกลิ่นรหัสถ้าคุณต้องการมัน
Joachim Sauer

69

หากคุณรู้ว่าคุณจะคืนค่าวัตถุสองชิ้นคุณสามารถใช้คู่ทั่วไปได้:

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;
    }
};

แก้ไขการดำเนินการตามที่กล่าวมาข้างต้นอย่างครบถ้วน:

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();

    }

}

หมายเหตุส่วนใหญ่เกี่ยวกับความเป็นสนิมด้วย Java & generics:

  • ทั้งaและbไม่เปลี่ยนรูป
  • makePairวิธีการแบบคงที่ช่วยให้คุณพิมพ์หม้อไอน้ำซึ่งผู้ประกอบการเพชรใน Java 7 จะทำให้น่ารำคาญน้อยลง มีงานบางอย่างที่จะทำให้เรื่องนี้ดีขึ้นอีก: generics แต่ตอนนี้ควรจะเรียบร้อยแล้ว (cf PECS)
  • hashcodeและequalsสร้างขึ้นโดย eclipse
  • เวลาในการคอมไพล์ castไม่เป็นไร แต่ดูเหมือนจะไม่ถูกต้องนัก
  • ฉันไม่แน่ใจว่าใช้สัญลักษณ์แทนหรือไม่ isInstanceจำเป็นต้องหรือไม่
  • ฉันเพิ่งเขียนสิ่งนี้เพื่อตอบสนองต่อความคิดเห็นเพื่อประกอบการอธิบายเท่านั้น

ฉันมักจะมีระดับนี้เคาะในแต่ละ codebase ฉันทำงาน ฉันยังเพิ่ม: การใช้ hashCode / เท่ากับและอาจเป็นวิธีคงที่ isInstance () และ cast ()
jamesh

แน่นอนว่ามีหลายวิธีที่จะทำให้คลาสนี้ฉลาดขึ้นและใช้งานได้สะดวกขึ้น เวอร์ชันด้านบนมีสิ่งที่เพียงพอในการประกาศแบบครั้งเดียว
David Hanak

@jamesh: คุณตั้งใจที่จะเขียน Pair ในรายละเอียดทั้งหมดที่นี่หรือไม่? ฉันต้องการทราบว่าดูเหมือนว่าหลังจากส่ง "การ hashCode / เท่ากับการใช้งานและอาจเป็นวิธีการคงที่ isInstance () และ cast ()" ขอบคุณ.
Qiang Li

@QiangLi - ฉันมักจะสร้างวิธีการ hashcode & เท่ากับ วิธีการอินสแตนซ์ isInstance ใช้สองคลาสและตรวจสอบให้แน่ใจว่า & a ของอินสแตนซ์นั้นเป็นอินสแตนซ์ของคลาสเหล่านั้น การร่ายจะใช้ Pair <?,>> และร่ายอย่างระมัดระวังไปยังการจับคู่ <A, B> การใช้งานควรจะค่อนข้างง่าย (คำใบ้: Class.cast (), Class.isInstance ())
jamesh

2
เป็นการPairใช้งานที่ดีมาก หนึ่งการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ ฉันต้องการให้: ClassCastExceptionเพิ่มข้อความถึง มิฉะนั้นการดีบักจะกลายเป็นฝันร้ายหากสิ่งนี้ล้มเหลวด้วยเหตุผลบางประการ (และ rawtypes ปราบปรามคำเตือนจะไม่จำเป็นถ้าคุณต้องการโยนไปPair<?,?>(ที่ทำงานเพราะคุณจำเป็นต้องใช้เพียงObjectวิธีการจากaและb) คุณคิดว่าผมปรับแต่งรหัส.
โจอาคิมซาวเออร์

25

ในกรณีที่วิธีการโทรของคุณเป็นแบบส่วนตัวหรือเรียกจากที่เดียวให้ลอง

return new Object[]{value1, value2};

ผู้โทรมีลักษณะดังนี้:

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

ตัวอย่างการจับคู่โดย David Hanak ไม่มีประโยชน์ทางไวยากรณ์และถูก จำกัด ไว้ที่สองค่า

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

และผู้โทรมีลักษณะดังนี้:

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

7
Pair มีประโยชน์ในการควบคุมระดับชั้นเรียน
Hlex

5
IMHO อย่าไปทางนี้ - การประกาศบอกว่าน้อยเกินไปเกี่ยวกับค่าส่งคืนที่คาดหวัง AFAIK เป็นที่ต้องการอย่างกว้างขวางมากขึ้นในการสร้างคลาสทั่วไปที่ระบุจำนวนพารามิเตอร์ที่จะถูกส่งคืนและประเภทของพารามิเตอร์เหล่านั้น Pair<T1, T2>, Tuple<T1, T2, T3>, Tuple<T1, T2, T3, T4>เป็นต้นจากนั้นระดับการใช้งานการแสดงจำนวนและประเภทของพารามิเตอร์Pair<int, String> temp = ...หรืออะไรก็ตาม
ToolmakerSteve

22

คุณสามารถใช้วิธีใดวิธีหนึ่งต่อไปนี้:

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) การใช้Array

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) การใช้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) การใช้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) ใช้คลาสคอนเทนเนอร์ของคุณเอง

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) การใช้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) การใช้คู่ของApache คอมมอนส์

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

ฉันมักจะจบลงด้วยการกำหนดคลาส n-Tuple เมื่อฉันรหัสใน Java ตัวอย่างเช่น

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;}
}

ฉันรู้ว่ามันน่าเกลียดนิดหน่อย แต่ก็ใช้งานได้และคุณเพียงแค่กำหนดประเภททูเปิลของคุณหนึ่งครั้ง Tuples เป็นสิ่งที่ Java ขาดไม่ได้จริงๆ

แก้ไข: ตัวอย่างของ David Hanak นั้นดูสง่างามยิ่งขึ้นเนื่องจากมันหลีกเลี่ยงการกำหนดตัวรับและยังคงรักษาวัตถุที่ไม่เปลี่ยนแปลง


9

ก่อน Java 5 ฉันจะยอมรับว่าการแก้ปัญหาแผนที่ไม่เหมาะ มันจะไม่ให้คุณตรวจสอบประเภทเวลาคอมไพล์ดังนั้นอาจทำให้เกิดปัญหาในขณะทำงาน อย่างไรก็ตามด้วย Java 5 เรามีประเภททั่วไป

ดังนั้นวิธีการของคุณอาจมีลักษณะเช่นนี้:

public Map<String, MyType> doStuff();

ประเภทของ MyType เป็นประเภทของวัตถุที่คุณจะกลับมา

โดยทั่วไปฉันคิดว่าการคืนค่าแผนที่เป็นวิธีแก้ปัญหาที่ถูกต้องในกรณีนี้เพราะนั่นคือสิ่งที่คุณต้องการส่งคืน - การจับคู่สตริงกับวัตถุ


สิ่งนี้จะไม่ทำงานหากมีชื่อใด ๆ ชนกัน รายการอาจมีรายการที่ซ้ำกัน แต่แผนที่ไม่สามารถ (มีคีย์ที่ซ้ำกัน)
tvanfosson

แน่นอน. ผมทำสมมติฐานขึ้นอยู่กับคำถาม - บางที unduely :)
kipz

แม้ว่าสมมติฐานของคุณจะเป็นจริงในกรณีนี้ แต่ฉันกำลังเดินเข้าไปในโดเมนของการปรับให้เหมาะสมก่อนวัยอันควร (ซึ่งฉันไม่ควรทำ)
Jagmal

6

อีกทางเลือกหนึ่งในสถานการณ์ที่ฉันต้องการคืนค่าจำนวนมากจากวิธีการบางครั้งฉันจะใช้กลไกการโทรกลับแทนคอนเทนเนอร์ สิ่งนี้ใช้ได้ดีมากในสถานการณ์ที่ฉันไม่สามารถระบุล่วงหน้าได้ว่าจะสร้างวัตถุจำนวนเท่าใด

ด้วยปัญหาเฉพาะของคุณมันจะมีลักษณะเช่นนี้:

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 );
    }
}

ขออภัยที่แสดงความคิดเห็นในคำตอบที่เก่าแก่ของคุณ แต่การโทรกลับเกี่ยวข้องกับการรวบรวมขยะได้อย่างไร แน่นอนว่าฉันไม่มีความเข้าใจที่ดีเกี่ยวกับการจัดการหน่วยความจำjavaถ้าคุณมี object Acall object B.getResult()และB.getResult()call A.finishResult()เป็น a callbackวัตถุBจะได้รับการเก็บขยะหรือมันอยู่รอบ ๆ จนเสร็จสิ้นหรือไม่? อาจเป็นคำถามที่โง่ แต่มันเป็นความสับสนขั้นพื้นฐานที่ฉันมี!
wired00

6

Apache Commons มีสิ่งอันดับสองและสามสำหรับสิ่งนี้:

  • ImmutablePair<L,R> คู่ที่ไม่เปลี่ยนรูปประกอบด้วยองค์ประกอบของวัตถุสองรายการ
  • ImmutableTriple<L,M,R> ทริปเปิลที่ไม่เปลี่ยนรูปประกอบด้วยองค์ประกอบของวัตถุสามอย่าง
  • MutablePair<L,R> คู่ที่ไม่แน่นอนซึ่งประกอบด้วยองค์ประกอบของวัตถุสองรายการ
  • MutableTriple<L,M,R> ทริปเปิลที่เปลี่ยนแปลงไม่ได้ประกอบด้วยองค์ประกอบวัตถุสามอย่าง
  • Pair<L,R> คู่ประกอบด้วยสององค์ประกอบ
  • Triple<L,M,R> สามประกอบด้วยสามองค์ประกอบ

ที่มา: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html



5

การใช้วัตถุรายการต่อไปนี้ตัวอย่าง:

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

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

5

เกี่ยวกับปัญหาเกี่ยวกับค่าส่งคืนหลายค่าโดยทั่วไปฉันมักจะใช้คลาสตัวช่วยขนาดเล็กที่ตัดค่าส่งคืนเดียวและส่งผ่านเป็นพารามิเตอร์ไปยังวิธีการ:

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; }
}

(สำหรับประเภทข้อมูลดั้งเดิมฉันใช้การเปลี่ยนแปลงเล็กน้อยเพื่อเก็บค่าโดยตรง)

วิธีการที่ต้องการคืนค่าหลายค่าจะได้รับการประกาศดังต่อไปนี้:

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

บางทีข้อเสียเปรียบที่สำคัญคือผู้โทรต้องเตรียมวัตถุส่งคืนล่วงหน้าในกรณีที่เขาต้องการใช้มัน (และวิธีการนั้นควรตรวจสอบพอยน์เตอร์พอยน์เตอร์)

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

ข้อดี (เปรียบเทียบกับโซลูชันอื่นที่เสนอ):

  • คุณไม่จำเป็นต้องสร้างการประกาศคลาสพิเศษสำหรับแต่ละวิธีและประเภทการส่งคืน
  • พารามิเตอร์จะได้รับชื่อจึงง่ายต่อการแยกความแตกต่างเมื่อดูที่เมธอดลายเซ็น
  • พิมพ์ความปลอดภัยสำหรับแต่ละพารามิเตอร์

ขอขอบคุณสำหรับวิธีแก้ปัญหาที่ให้ชื่อและประเภทความปลอดภัยแก่ค่าที่ส่งคืนแต่ละรายการโดยไม่ต้องมีการประกาศคลาสต่อชุดประเภทค่าที่ส่งคืน
ToolmakerSteve

3

โซลูชันที่เป็นไปได้ทั้งหมดจะเป็น kludge (เช่นออบเจ็กต์คอนเทนเนอร์แนวคิด HashMap ของคุณ“ ค่าส่งคืนหลายค่า” ตามที่รับรู้ผ่านอาร์เรย์) ฉันขอแนะนำให้สร้างรายการที่คั่นด้วยเครื่องหมายจุลภาคใหม่จากรายการที่ส่งคืน รหัสจะจบลงด้วยการทำความสะอาดมากขึ้น


ฉันเห็นด้วยกับคุณในเรื่องนี้ แต่ถ้าฉันทำเช่นนั้นฉันจะสิ้นสุดการวนซ้ำสองครั้ง (จริง ๆ แล้วฉันกำลังสร้างองค์ประกอบของรายการหนึ่งต่อหนึ่งในวิธีการที่มีอยู่)
Jagmal

1
@ Jagmal: คุณอาจวนซ้ำสองครั้ง แต่ก็ไม่สำคัญว่าจะใช้เวลานานเท่าไหร่ (ดูคำตอบ gizmos)
Joachim Sauer

1
ใช่อย่าพยายามเพิ่มประสิทธิภาพรหัสของคุณจนกว่าคุณจะต้อง Gizmo นั้นถูกต้องมากเกี่ยวกับเรื่องนั้น
Bombe

3

ทำให้มันง่ายและสร้างคลาสสำหรับสถานการณ์ผลลัพธ์ที่หลากหลาย ตัวอย่างนี้ยอมรับ ArrayList และข้อความจาก databasehelper getInfo

ที่คุณเรียกรูทีนที่ส่งคืนค่าหลายค่าที่คุณใช้รหัส:

multResult res = mydb.getInfo(); 

ในงานประจำ getInfo คุณรหัส:

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

และกำหนด class multResult ด้วย:

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

อย่างที่ฉันเห็นมันมีสามตัวเลือกจริงๆที่นี่และการแก้ปัญหาขึ้นอยู่กับบริบท คุณสามารถเลือกที่จะใช้การสร้างชื่อในวิธีการที่สร้างรายการ นี่คือตัวเลือกที่คุณเลือก แต่ฉันไม่คิดว่ามันจะดีที่สุด คุณกำลังสร้างการเชื่อมต่อในวิธีผู้ผลิตกับวิธีการบริโภคที่ไม่จำเป็นต้องมีอยู่ ผู้โทรรายอื่นอาจไม่ต้องการข้อมูลเพิ่มเติมและคุณจะคำนวณข้อมูลเพิ่มเติมสำหรับผู้โทรเหล่านี้

หรือคุณอาจมีวิธีการโทรคำนวณชื่อ หากมีผู้โทรเพียงคนเดียวที่ต้องการข้อมูลนี้คุณสามารถหยุดได้ คุณไม่มีการพึ่งพาเป็นพิเศษและในขณะที่มีการคำนวณเพิ่มเติมเล็กน้อยคุณก็หลีกเลี่ยงการสร้างวิธีการก่อสร้างที่เฉพาะเจาะจงเกินไป นี่เป็นการแลกเปลี่ยนที่ดี

สุดท้ายคุณอาจมีรายชื่อตัวเองรับผิดชอบในการสร้างชื่อ นี่คือเส้นทางที่ฉันจะไปหากการคำนวณจำเป็นต้องมีผู้โทรมากกว่าหนึ่งคน ฉันคิดว่าสิ่งนี้ทำให้เกิดความรับผิดชอบในการสร้างชื่อด้วยชั้นเรียนที่เกี่ยวข้องกับวัตถุมากที่สุด

ในกรณีหลังโซลูชันของฉันจะสร้างคลาสรายการพิเศษที่ส่งคืนสตริงที่คั่นด้วยเครื่องหมายจุลภาคของชื่อของวัตถุที่มี ทำให้คลาสฉลาดพอที่จะสร้างสตริงชื่อได้ทันทีขณะที่วัตถุถูกเพิ่มและลบออกจากมัน จากนั้นส่งคืนอินสแตนซ์ของรายการนี้และเรียกใช้วิธีการสร้างชื่อตามต้องการ แม้ว่ามันอาจจะมีประสิทธิภาพเกือบ (และง่ายกว่า) ในการคำนวณการหน่วงเวลาของชื่อจนกว่าจะเป็นครั้งแรกที่มีการเรียกเมธอดและเก็บไว้ (การโหลดแบบสันหลังยาว) หากคุณเพิ่ม / ลบวัตถุคุณเพียงแค่ลบค่าที่คำนวณได้และให้มันคำนวณใหม่ในการโทรครั้งต่อไป


2

สามารถทำบางสิ่งเช่นทูเปิลในภาษาไดนามิก (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]);
}
}

และใช้แบบนี้

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


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

2

ฉันทำตามวิธีที่คล้ายกันกว่าที่อธิบายไว้ในคำตอบอื่น ๆ ด้วยการปรับแต่งเล็กน้อยตามข้อกำหนดที่ฉันมีโดยทั่วไปฉันสร้างคลาสต่อไปนี้ (ในกรณีทุกอย่างคือ 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);
    }
}

จากนั้นความต้องการของฉันนั้นง่ายในคลาสที่เก็บข้อมูลที่มาถึงฐานข้อมูลสำหรับวิธีการรับมากกว่าการดึงข้อมูลจากฐานข้อมูลฉันต้องตรวจสอบว่ามันล้มเหลวหรือประสบความสำเร็จแล้วถ้าประสบความสำเร็จฉันต้องเล่นกับรายการกลับ หากล้มเหลวให้หยุดดำเนินการและแจ้งข้อผิดพลาด

ตัวอย่างเช่นวิธีการของฉันเป็นเช่นนี้

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
}

โดยที่ ResultMessage เป็นเพียงคลาสที่มีสองฟิลด์ (รหัส / ข้อความ) และลูกค้าคือคลาสใด ๆ ที่มีฟิลด์จำนวนมากที่มาจาก DB

จากนั้นเพื่อตรวจสอบผลฉันเพิ่งทำสิ่งนี้:

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

ใน C ++ (STL) มีคลาสคู่สำหรับการรวมสองวัตถุ ใน Java Generics คลาสคู่ไม่มีให้บริการแม้ว่าจะมีความต้องการบางอย่างสำหรับมัน คุณสามารถนำไปใช้ด้วยตนเองได้อย่างง่ายดาย

ฉันเห็นด้วยกับคำตอบอื่น ๆ ที่ว่าถ้าคุณต้องการคืนวัตถุสองชิ้นหรือมากกว่าจากวิธีการจะเป็นการดีกว่าที่จะแค็ปซูลพวกมันในชั้นเรียน


1

ทำไมไม่สร้างWhateverFunctionResultวัตถุที่มีผลลัพธ์ของคุณและตรรกะที่จำเป็นในการแยกผลลัพธ์เหล่านี้ซ้ำแล้วซ้ำอีกเป็นต้นดูเหมือนว่าสำหรับฉัน:

  1. วัตถุผลลัพธ์เหล่านี้มีการเชื่อมโยงอย่างใกล้ชิด / เกี่ยวข้องและอยู่ด้วยกันหรือ:
  2. มันไม่เกี่ยวข้องกันในกรณีนี้ฟังก์ชั่นของคุณไม่ได้นิยามไว้อย่างชัดเจนในแง่ของสิ่งที่พยายามทำ (เช่นการทำสองสิ่งที่แตกต่างกัน)

ฉันเห็นปัญหาประเภทนี้หมดไปเรื่อย ๆ อย่ากลัวที่จะสร้างคลาสคอนเทนเนอร์ / ผลลัพธ์ของคุณเองที่มีข้อมูลและฟังก์ชันการทำงานที่เกี่ยวข้องเพื่อจัดการสิ่งนี้ หากคุณเพียงแค่ส่งเนื้อหาไปรอบ ๆ ในHashMapหรือที่คล้ายกันจากนั้นลูกค้าของคุณจะต้องดึงแผนที่นี้ออกจากกันและคลี่คลายเนื้อหาทุกครั้งที่พวกเขาต้องการใช้ผลลัพธ์


เพราะมันเป็น PITA ที่จะต้องกำหนดคลาสเมื่อใดก็ตามที่คุณต้องการคืนค่าหลายค่าเพียงเพราะภาษาขาดคุณสมบัติที่มีประโยชน์นี้;) แต่อย่างจริงจังสิ่งที่คุณแนะนำมักจะคุ้มค่าที่จะทำ
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

นี่ไม่ใช่การตอบคำถามอย่างแน่นอน แต่เนื่องจากวิธีแก้ปัญหาทุกข้อในที่นี้มีข้อบกพร่องบางประการฉันขอแนะนำให้ลองสร้างรหัสใหม่อีกครั้งเพื่อให้คุณต้องส่งคืนค่าเดียว

กรณีที่หนึ่ง

คุณต้องการสิ่งที่อยู่ข้างในและข้างนอกวิธีการของคุณ ทำไมไม่คำนวณนอกและส่งผ่านไปยังวิธีการ

แทน:

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

ลอง:

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

สิ่งนี้จะครอบคลุมความต้องการส่วนใหญ่ของคุณเนื่องจากในสถานการณ์ส่วนใหญ่ค่าหนึ่งจะถูกสร้างขึ้นก่อนที่ค่าอื่น ๆ และคุณสามารถแบ่งการสร้างเป็นสองวิธี ข้อเสียเปรียบคือเมธอดcreateThingsBนั้นมีพารามิเตอร์เพิ่มเติมเมื่อเปรียบเทียบกับcreateThingsและคุณอาจผ่านรายการพารามิเตอร์เดียวกันสองครั้งไปยังวิธีอื่น


กรณีที่สอง

วิธีแก้ปัญหาที่ชัดเจนที่สุดและรุ่นที่ง่ายกว่าของเคสหนึ่ง มันเป็นไปไม่ได้เสมอไป แต่อาจจะสร้างค่าทั้งสองอย่างเป็นอิสระจากกันได้ไหม

แทน:

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

ลอง:

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

เพื่อให้มีประโยชน์มากขึ้นวิธีการทั้งสองนี้สามารถแบ่งปันตรรกะทั่วไปบางอย่าง:

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

0

ส่งรายการไปยังเมธอดของคุณและเติมจากนั้นส่งคืนสตริงด้วยชื่อดังนี้:

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

จากนั้นเรียกมันว่าสิ่งนี้:

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

-2

ฉันได้รับการใช้วิธีการขั้นพื้นฐานมากในการจัดการกับปัญหาผลตอบแทนหลาย ๆ มันให้บริการวัตถุประสงค์และหลีกเลี่ยงความซับซ้อน

ฉันเรียกมันว่าวิธีการแยกสตริง

และมีประสิทธิภาพเนื่องจากสามารถคืนค่าหลายประเภทเช่น int, double, char, string ฯลฯ

ในวิธีการนี้เราใช้ประโยชน์จากสตริงที่ไม่น่าจะเกิดขึ้นโดยทั่วไป เราเรียกมันว่าเป็นตัวคั่น ตัวคั่นนี้จะถูกใช้เพื่อแยกค่าต่าง ๆ เมื่อใช้ในฟังก์ชั่น

ตัวอย่างเช่นเราจะมีผลตอบแทนสุดท้ายของเราเป็น (ตัวอย่าง) intValue separator doubleValue separator ... และจากนั้นการใช้สตริงนี้เราจะดึงข้อมูลทั้งหมดที่จำเป็นซึ่งอาจเป็นประเภทที่แตกต่างเช่นกัน

รหัสต่อไปนี้จะแสดงการทำงานของแนวคิดนี้

ตัวคั่นที่ใช้คือ! @ #และ 3 ค่าจะถูกส่งคืน intVal, doubleVal และ 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);
            }
        }

เอาท์พุท

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

3
จุ๊บ กลิ่นรหัสมาก การแยกวิเคราะห์แทนที่จะใช้คุณสมบัติเชิงวัตถุ IMO หนึ่งในตัวอย่างที่แย่ที่สุดของการเข้ารหัสที่ฉันเคยเห็น นอกจากว่าคุณกำลังอธิบายสถานการณ์ที่คุณต้องผ่านหลายค่าระหว่างสองโปรแกรมอิสระหรือการสื่อสารระหว่างกระบวนการอื่น ๆ และอย่างใดขาดการเข้าถึงกลไกที่เหมาะสมสำหรับการทำเช่นนั้น (json หรืออื่น ๆ )
ToolmakerSteve

-4

ใน C คุณสามารถทำได้โดยส่งพอยน์เตอร์ไปยังตัวยึดสำหรับผลลัพธ์เป็นอาร์กิวเมนต์:

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

ลองทำสิ่งที่คล้ายกันใน 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
อย่างไรก็ตามในภาษา OO โดยทั่วไปถือว่าดีกว่าที่จะทำสิ่งที่หลาย ๆ คนแนะนำไปแล้วสี่ปีก่อนคำตอบนี้: จัดกลุ่มค่าที่เกี่ยวข้องสองค่าเป็นวัตถุเดียว (คู่ tuple หรือนิยามคลาสแบบกำหนดเอง) จากนั้นมีรายการของสิ่งเหล่านั้น วัตถุ การทำเช่นนี้หลีกเลี่ยงความต้องการที่จะผ่านหลายรายการ สิ่งนี้มีความสำคัญอย่างยิ่งหากจำเป็นต้องผ่านคู่ (หนึ่งองค์ประกอบของแต่ละรายการของคุณ) ไปยังวิธีการอื่นเพื่อการประมวลผลเพิ่มเติม
ToolmakerSteve

@ToolmakerSteve ชี้แจง: รายการมีจุดประสงค์ที่จะมีองค์ประกอบหนึ่งอย่างแน่นอนแต่ละรายการและเป็นเพียงวิธีการใช้งานอะนาล็อกเพื่อส่งผ่านตัวชี้ พวกเขาไม่ได้ตั้งใจจะรวบรวมผลลัพธ์หลายรายการหรือแม้กระทั่งใช้มากกว่าสองบรรทัดหลังจากการเรียกใช้เมธอด
Adrian Panasiuk

-5

ส่งแฮชเข้าไปในวิธีการและเติมให้มัน ......

โมฆะสาธารณะ buildResponse (ข้อมูลสตริงการตอบสนองแผนที่);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.