java: ฉันจะทำการแคสต์ตัวแปรแบบไดนามิกจากประเภทหนึ่งไปยังอีกประเภทหนึ่งได้อย่างไร


85

ฉันต้องการทำการแคสต์แบบไดนามิกสำหรับตัวแปร Java ประเภทการหล่อจะถูกเก็บไว้ในตัวแปรอื่น

นี่คือการคัดเลือกนักแสดงปกติ:

 String a = (String) 5;

นี่คือสิ่งที่ฉันต้องการ:

 String theType = 'String';
 String a = (theType) 5;

เป็นไปได้หรือไม่และถ้าเป็นเช่นนั้นได้อย่างไร ขอบคุณ!

อัปเดต

ฉันกำลังพยายามเติมข้อมูลในชั้นเรียนด้วยHashMapที่ฉันได้รับ

นี่คือตัวสร้าง:

public ConnectParams(HashMap<String,Object> obj) {

    for (Map.Entry<String, Object> entry : obj.entrySet()) {
        try {
            Field f =  this.getClass().getField(entry.getKey());                
            f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
        } catch (NoSuchFieldException ex) {
            log.error("did not find field '" + entry.getKey() + '"');
        } catch (IllegalAccessException ex) {
            log.error(ex.getMessage());         
        }
    }

}

ปัญหาคือตัวแปรของคลาสบางตัวเป็นประเภทDoubleและหากได้รับหมายเลข 3 ก็จะเห็นว่าเป็นIntegerและฉันมีปัญหาประเภท


นั่นไม่สมเหตุสมผลเลย คุณต้องการให้ชื่อตัวแปรเป็นชนิดเพื่อโยนสตริงเป็นสตริงหรือไม่? อะไร?
cletus

3
ฉันไม่รู้คำตอบ แต่ฉันกลัวว่าสิ่งนี้จะกลายเป็นการบำรุงรักษา ... แค่เรียนรู้ Java ด้วยตัวเอง แต่ฉันจะหลีกเลี่ยงสถานการณ์ที่ต้องใช้แนวทางเช่นนี้ ฉันค่อนข้างมั่นใจว่าสิ่งที่คุณทำสามารถนำไปใช้ในทางที่ดีขึ้นได้ ... แค่ 2 เซ็นต์ของฉัน
Sejanus

โอเคฉันจะให้ข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ฉันพยายามบรรลุ
ufk

ยังอัปเดตคำตอบของฉันด้านล่าง!
user85421

คำตอบ:


14

เกี่ยวกับการอัปเดตของคุณวิธีเดียวที่จะแก้ปัญหานี้ใน Java คือการเขียนโค้ดที่ครอบคลุมทุกกรณีที่มีจำนวนมากifและelseและinstanceofการแสดงออก สิ่งที่คุณพยายามทำดูเหมือนว่าใช้ในการเขียนโปรแกรมด้วยภาษาไดนามิก ในภาษาคงที่สิ่งที่คุณพยายามทำแทบจะเป็นไปไม่ได้และอาจมีคนเลือกแนวทางที่แตกต่างไปจากเดิมโดยสิ้นเชิงสำหรับสิ่งที่คุณพยายามทำ ภาษาคงที่ไม่ยืดหยุ่นเท่าภาษาแบบไดนามิก :)

ตัวอย่างที่ดีของแนวทางปฏิบัติที่ดีที่สุดของ Java คือคำตอบของ BalusC (ie ObjectConverter) และคำตอบโดย Andreas_D (ie Adapter) ด้านล่าง


ที่ไม่สมเหตุสมผลใน

String a = (theType) 5;

ประเภทของaถูกผูกไว้แบบคงที่Stringดังนั้นจึงไม่สมเหตุสมผลที่จะมีการร่ายแบบไดนามิกกับประเภทคงที่นี้

ป.ล. : บรรทัดแรกของตัวอย่างของคุณสามารถเขียนเป็นClass<String> stringClass = String.class;แต่กระนั้นคุณไม่สามารถใช้stringClassเพื่อแคสต์ตัวแปรได้


ฉันหวังว่าการอัปเดตที่ฉันโพสต์จะอธิบายสิ่งที่ฉันพยายามทำ ฉันมาจากพื้นหลัง php ดังนั้นบางทีสิ่งนี้ไม่สามารถทำได้ใน java
ufk

แน่นอนใน Java คุณไม่สามารถเป็นแบบไดนามิกนั้นได้โปรดดูการอัปเดตของฉันด้วย
akuhn

ดูคำตอบของ BalusC ด้านล่างนี่คือความยาว (และความเจ็บปวด) ที่คุณต้องไป ...
akuhn

ฉันรู้ว่ามันสายไปแล้ว แต่ฉันคิดว่าเขาหมายถึง [thetype] a = (thetype) some_object; ซึ่งไม่มีจุดหมายเพราะคุณสามารถทำ Object a = some_object ได้ดี
George Xavier

120

ใช่มันเป็นไปได้โดยใช้การสะท้อนกลับ

Object something = "something";
String theType = "java.lang.String";
Class<?> theClass = Class.forName(theType);
Object obj = theClass.cast(something);

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

หากคุณต้องการรับคลาสที่กำหนดNumberตัวอย่างเช่น:

Object something = new Integer(123);
String theType = "java.lang.Number";
Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class);
Number obj = theClass.cast(something);

แต่ยังไม่มีประเด็นที่จะทำเช่นนั้นคุณสามารถส่งไปที่ Numberแต่ยังคงมีจุดใดที่ทำมันเป็นเช่นนั้นคุณก็สามารถโยนไป

การหล่อวัตถุไม่เปลี่ยนแปลงอะไร มันเป็นเพียงวิธีที่คอมไพเลอร์ปฏิบัติต่อมัน
เหตุผลเดียวที่ต้องทำสิ่งนั้นคือการตรวจสอบว่าวัตถุนั้นเป็นอินสแตนซ์ของคลาสที่กำหนดหรือคลาสย่อยใด ๆ ของมัน แต่จะทำได้ดีกว่าโดยใช้instanceofหรือClass.isInstance()หรือ

อัปเดต

ตามสุดท้ายของคุณปรับปรุงปัญหาที่แท้จริงคือการที่คุณมีIntegerในของคุณที่ควรจะได้รับมอบหมายให้HashMap Doubleสิ่งที่คุณสามารถทำได้ในกรณีนี้คือตรวจสอบประเภทของฟิลด์และใช้xxxValue()วิธีการของNumber

...
Field f =  this.getClass().getField(entry.getKey());
Object value = entry.getValue();
if (Integer.class.isAssignableFrom(f.getType())) {
    value = Integer.valueOf(((Number) entry.getValue()).intValue());
} else if (Double.class.isAssignableFrom(f.getType())) {
    value = Double.valueOf(((Number) entry.getValue()).doubleValue());
} // other cases as needed (Long, Float, ...)
f.set(this, value);
...

(ไม่แน่ใจว่าชอบไอเดียพิมพ์ผิดหรือเปล่าMap)


22

คุณจะต้องเขียนประเภทObjectConverterนี้ สิ่งนี้ทำได้ถ้าคุณมีทั้งออบเจ็กต์ที่คุณต้องการแปลงและคุณรู้คลาสเป้าหมายที่คุณต้องการแปลง Field#getDeclaringClass()เฉพาะในกรณีนี้คุณจะได้รับระดับเป้าหมายโดย

คุณสามารถค้นหาที่นี่ObjectConverterตัวอย่างของการดังกล่าว ควรให้แนวคิดพื้นฐานแก่คุณ หากคุณต้องการความเป็นไปได้ในการแปลงมากขึ้นเพียงเพิ่มวิธีการเพิ่มเติมด้วยอาร์กิวเมนต์และประเภทการส่งคืนที่ต้องการ


@BalusC - ฉันคิดว่าโค้ด ObjectConverter น่าสนใจคุณช่วยอธิบายกรณีการใช้งานได้ไหม
srini.venigalla

จะมีประโยชน์ในกรณีที่ต้องการการประชุมมากกว่าการกำหนดค่าและประเภทแหล่งที่มาไม่ตรงกับประเภทเป้าหมาย ฉันใช้มันเมื่อ 2-3 ปีก่อนในเฟรมเวิร์ก ORM และ MVC ของฉัน (บริสุทธิ์เพื่องานอดิเรก) ดูข้อความนำหน้าของบทความบล็อกด้วย
BalusC

12

คุณสามารถทำได้โดยใช้Class.cast()เมธอดซึ่งส่งพารามิเตอร์ที่ให้มาแบบไดนามิกเป็นประเภทของอินสแตนซ์คลาสที่คุณมี ในการรับอินสแตนซ์คลาสของฟิลด์ใดฟิลด์หนึ่งคุณใช้getType()เมธอดในฟิลด์ที่เป็นปัญหา ฉันได้ยกตัวอย่างด้านล่างนี้แล้ว แต่โปรดทราบว่าการจัดการข้อผิดพลาดทั้งหมดจะละเว้นและไม่ควรใช้โดยไม่แก้ไข

public class Test {

    public String var1;
    public Integer var2;
}

public class Main {

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("var1", "test");
        map.put("var2", 1);

        Test t = new Test();

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Field f = Test.class.getField(entry.getKey());

            f.set(t, f.getType().cast(entry.getValue()));
        }

        System.out.println(t.var1);
        System.out.println(t.var2);
    }
}

1
จะเกิดอะไรขึ้นถ้าประเภทรายการไม่ใช่ประเภทพิเศษของประเภทฟิลด์เลย? จากนั้นคุณจะต้องแปลงโดยใช้โปรแกรมจริงๆ
BalusC

7

คุณสามารถเขียน CastMethod ง่ายๆได้ดังวิธีการด้านล่าง

private <T> T castObject(Class<T> clazz, Object object) {
  return (T) object;
}

ในวิธีการของคุณคุณควรใช้มันเช่น

public ConnectParams(HashMap<String,Object> object) {

for (Map.Entry<String, Object> entry : object.entrySet()) {
    try {
        Field f =  this.getClass().getField(entry.getKey());                
        f.set(this, castObject(entry.getValue().getClass(), entry.getValue()); /* <= CASTING PROBLEM */
    } catch (NoSuchFieldException ex) {
        log.error("did not find field '" + entry.getKey() + '"');
    } catch (IllegalAccessException ex) {    
        log.error(ex.getMessage());          
    }    
}

}

ใช่ฉันคิดว่าคำตอบนี้คือสิ่งที่ผู้ถามต้องการ เขา / เธอเพิ่งได้รับคลาส <?> และต้องการแปลงอินสแตนซ์เป็นคลาส "?" โดยค่าเริ่มต้น java ไม่รองรับสิ่งนี้ แต่การใช้ <T> เราสามารถทำได้
ruiruige1991

@ ruiruige1991 นี่มันผิด T ซึ่งในกรณีนี้เป็นแบบทั่วไป Generics ไม่ทำอะไรเลยระหว่างรันไทม์ (T) blah จะเป็น (Object) blah ระหว่างรันไทม์เนื่องจากการลบประเภท ในระยะสั้น generics -> compile-time และไม่มีผลระหว่างรันไทม์ เนื่องจาก dynamic -> runtime และ generics -> compile time, generics ก็ไม่มีประโยชน์
George Xavier

5

มันใช้งานได้และยังมีรูปแบบทั่วไปสำหรับแนวทางของคุณ: รูปแบบอะแดปเตอร์รูปแบบอะแดปเตอร์แต่แน่นอนว่า (1) ใช้ไม่ได้สำหรับการหล่อ java primitives ไปยังวัตถุและ (2) คลาสจะต้องปรับเปลี่ยนได้ (โดยปกติจะใช้อินเทอร์เฟซที่กำหนดเอง)

ด้วยรูปแบบนี้คุณสามารถทำสิ่งต่างๆเช่น:

Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);

และเมธอด getAdapter ในคลาส Wolf:

public Object getAdapter(Class clazz) {
  if (clazz.equals(Sheep.class)) {
    // return a Sheep implementation
    return getWolfDressedAsSheep(this);
  }

  if (clazz.equals(String.class)) {
    // return a String
    return this.getName();
  }

  return null; // not adaptable
}

ความคิดพิเศษสำหรับคุณ - นั่นเป็นไปไม่ได้ คุณไม่สามารถใช้ค่า String ในการแคสต์


2

ปัญหาของคุณไม่ใช่การขาด "ไดนามิกแคสต์" แคสต์IntegerถึงDoubleไม่ได้เลย ดูเหมือนว่าคุณต้องการให้ Java เป็นอ็อบเจ็กต์ประเภทหนึ่งซึ่งเป็นฟิลด์ของประเภทที่อาจเข้ากันไม่ได้และให้มันหาวิธีการแปลงระหว่างประเภทโดยอัตโนมัติ

สิ่งนี้เป็นสิ่งที่น่ารังเกียจสำหรับภาษาที่พิมพ์อย่างรุนแรงเช่น Java และ IMO ด้วยเหตุผลที่ดีมาก

คุณกำลังพยายามทำอะไรอยู่? การใช้ภาพสะท้อนทั้งหมดนั้นดูน่าสนใจ


@name: เกี่ยวกับการแก้ไขที่คุณแนะนำ: โปรดทราบว่าฉันไม่ได้พูดถึงค่าดั้งเดิม แต่เกี่ยวกับคลาสของ wrapper (แสดงโดยการใช้อักษรตัวพิมพ์ใหญ่และการจัดรูปแบบเป็นรหัส) และการแคสต์ระหว่างสิ่งเหล่านั้นเป็นไปไม่ได้อย่างแน่นอน
ไมเคิลบอร์กเวิร์ด

1

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


1

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

print ++($foo = '99');  # prints '100'
print ++($foo = 'a0');  # prints 'a1'

ใน Java สิ่งนี้ทำได้ดีกว่า (IMHO) โดยใช้วิธีที่เรียกว่า "cross-casting" ด้วยการหล่อแบบไขว้จะใช้การสะท้อนในแคชของตัวสร้างและวิธีการที่โหลดแบบเกียจคร้านซึ่งค้นพบแบบไดนามิกผ่านวิธีการคงที่ต่อไปนี้:

Object fromString (String value, Class targetClass)

น่าเสียดายที่ไม่มีเมธอด Java ในตัวเช่น Class.cast () จะทำเช่นนี้สำหรับ String to BigDecimal หรือ String to Integer หรือการแปลงอื่น ๆ ที่ไม่มีลำดับชั้นของคลาสที่รองรับ ในส่วนของฉันประเด็นคือการจัดหาวิธีที่มีพลวัตอย่างเต็มที่เพื่อให้บรรลุสิ่งนี้ซึ่งฉันไม่คิดว่าการอ้างอิงก่อนหน้านี้เป็นแนวทางที่ถูกต้อง - โดยต้องเขียนโค้ดทุก Conversion พูดง่ายๆคือการใช้งานเป็นเพียงการแคสต์จากสตริงหากถูกกฎหมาย / เป็นไปได้

ดังนั้นวิธีแก้ปัญหาคือการไตร่ตรองอย่างง่าย ๆ โดยมองหาสมาชิกสาธารณะของอย่างใดอย่างหนึ่ง:

STRING_CLASS_ARRAY = (คลาสใหม่ [] {String.class});

ก) สมาชิกสมาชิก = targetClass.getMethod (method.getName (), STRING_CLASS_ARRAY); b) สมาชิกสมาชิก = targetClass.getConstructor (STRING_CLASS_ARRAY);

คุณจะพบว่าพื้นฐานทั้งหมด (Integer, Long, ฯลฯ ) และพื้นฐานทั้งหมด (BigInteger, BigDecimal ฯลฯ ) และแม้แต่ java.regex รูปแบบทั้งหมดได้รับการคุ้มครองผ่านแนวทางนี้ ฉันได้ใช้สิ่งนี้กับความสำเร็จอย่างมากในโครงการการผลิตที่มีอินพุตค่าสตริงตามอำเภอใจจำนวนมากซึ่งจำเป็นต้องมีการตรวจสอบที่เข้มงวดมากขึ้น ในแนวทางนี้หากไม่มีเมธอดหรือเมื่อมีการเรียกใช้เมธอดข้อยกเว้นจะถูกโยนทิ้ง (เนื่องจากเป็นค่าที่ไม่ถูกต้องเช่นอินพุตที่ไม่ใช่ตัวเลขไปยัง BigDecimal หรือ RegEx ที่ไม่ถูกต้องสำหรับรูปแบบ) ที่ให้การตรวจสอบเฉพาะสำหรับ ตรรกะโดยธรรมชาติของคลาสเป้าหมาย

มีข้อเสียบางประการดังนี้:

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

static Integer getInteger(String nm)
      Determines the integer value of the system property with the specified name.

วิธีการข้างต้นไม่มีอะไรเกี่ยวข้องกับจำนวนเต็มในฐานะวัตถุที่ห่อหุ้มวัตถุ ints Reflection จะพบว่านี่เป็นตัวเลือกที่เป็นไปได้สำหรับการสร้าง Integer จาก String อย่างไม่ถูกต้องเมื่อเทียบกับ decode, valueof และ constructor Members ซึ่งทั้งหมดนี้เหมาะสำหรับการแปลง String โดยพลการส่วนใหญ่ที่คุณไม่สามารถควบคุมข้อมูลอินพุตของคุณได้ แต่ต้องการ ทราบว่าเป็นไปได้หรือไม่จำนวนเต็ม

ในการแก้ไขข้างต้นการมองหาวิธีการที่ส่งข้อยกเว้นเป็นการเริ่มต้นที่ดีเนื่องจากค่าอินพุตที่ไม่ถูกต้องซึ่งสร้างอินสแตนซ์ของวัตถุดังกล่าวควรทำให้เกิดข้อยกเว้น น่าเสียดายที่การใช้งานแตกต่างกันไปตามการประกาศข้อยกเว้นว่าเลือกไว้หรือไม่ Integer.valueOf (String) พ่น NumberFormatException ที่ตรวจสอบแล้วเช่น แต่ไม่พบข้อยกเว้น Pattern.compile () ในระหว่างการค้นหาการสะท้อน อีกครั้งไม่ใช่ความล้มเหลวของแนวทาง "การหล่อข้ามสาย" แบบไดนามิกนี้ฉันคิดว่ามากเท่ากับการใช้งานที่ไม่ได้มาตรฐานสำหรับการประกาศข้อยกเว้นในวิธีการสร้างวัตถุ

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

ไชโย


1

นี่เป็นโพสต์เก่า แต่ฉันคิดว่าฉันสามารถมีส่วนร่วมกับมันได้

คุณสามารถทำสิ่งนี้ได้ตลอดเวลา:

package com.dyna.test;  

import java.io.File;  
import java.lang.reflect.Constructor;  

public class DynamicClass{  

  @SuppressWarnings("unchecked")  
  public Object castDynamicClass(String className, String value){  
    Class<?> dynamicClass;  

    try  
    {  
      //We get the actual .class object associated with the specified name  
      dynamicClass = Class.forName(className);  



    /* We get the constructor that received only 
     a String as a parameter, since the value to be used is a String, but we could
easily change this to be "dynamic" as well, getting the Constructor signature from
the same datasource we get the values from */ 


      Constructor<?> cons =  
        (Constructor<?>) dynamicClass.getConstructor(new Class<?>[]{String.class});  

      /*We generate our object, without knowing until runtime 
 what type it will be, and we place it in an Object as 
 any Java object extends the Object class) */  
      Object object = (Object) cons.newInstance(new Object[]{value});  

      return object;  
    }  
    catch (Exception e)  
    {  
      e.printStackTrace();  
    }  
    return null;  
  }  

  public static void main(String[] args)  
  {   
    DynamicClass dynaClass = new DynamicClass();  

    /* 
 We specify the type of class that should be used to represent 
 the value "3.0", in this case a Double. Both these parameters 
 you can get from a file, or a network stream for example. */  
    System.out.println(dynaClass.castDynamicClass("java.lang.Double", "3.0"));  

    /* 
We specify a different value and type, and it will work as 
 expected, printing 3.0 in the above case and the test path in the one below, as the Double.toString() and 
 File.toString() would do. */  
    System.out.println(dynaClass.castDynamicClass("java.io.File", "C:\\testpath"));  
  }  

แน่นอนว่านี่ไม่ใช่การแคสต์แบบไดนามิกเหมือนในภาษาอื่น ๆ (เช่น Python) เนื่องจาก java เป็นภาษาที่พิมพ์แบบคงที่ อย่างไรก็ตามวิธีนี้สามารถแก้ปัญหาบางกรณีที่คุณต้องโหลดข้อมูลบางอย่างด้วยวิธีต่างๆกันขึ้นอยู่กับตัวระบุบางตัว นอกจากนี้ส่วนที่คุณได้รับตัวสร้างที่มีพารามิเตอร์ String อาจทำให้ยืดหยุ่นได้มากขึ้นโดยการส่งผ่านพารามิเตอร์จากแหล่งข้อมูลเดียวกัน เช่นจากไฟล์คุณจะได้รับลายเซ็นตัวสร้างที่คุณต้องการใช้และรายการของค่าที่จะใช้วิธีที่คุณจับคู่กล่าวว่าพารามิเตอร์ตัวแรกคือสตริงโดยที่อ็อบเจ็กต์แรกหล่อเป็นสตริง อ็อบเจ็กต์ถัดไปคือจำนวนเต็ม ฯลฯ แต่เมื่อดำเนินการตามโปรแกรมของคุณคุณจะได้รับอ็อบเจ็กต์ File ก่อนแล้วจึงเป็น Double เป็นต้น

ด้วยวิธีนี้คุณสามารถอธิบายกรณีเหล่านั้นและทำการแคสต์แบบ "ไดนามิก" ได้ทันที

หวังว่านี่จะช่วยทุกคนเพราะสิ่งนี้จะปรากฏในการค้นหาของ Google


0

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

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

ด้านล่างฉันจะแสดงวิธีอื่นอีก 2 วิธีในการ "แคสต์แบบไดนามิก"

// Method 1.
mFragment = getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
switch (mUnitNum) {
case 0:
    ((MyFragment0) mFragment).sortNames(sortOptionNum);
    break;
case 1:
    ((MyFragment1) mFragment).sortNames(sortOptionNum);
    break;
case 2:
    ((MyFragment2) mFragment).sortNames(sortOptionNum);
    break;
}

และวิธีการที่ฉันใช้ในปัจจุบัน

// Method 2.
mSuperFragment = (MySuperFragment) getFragmentManager().findFragmentByTag(MyHelper.getName(mUnitNum));
mSuperFragment.sortNames(sortOptionNum);

คุณไม่จำเป็นต้องแคสต์แบบไดนามิกเลย สูตรของคุณในการมีซูเปอร์คลาสด้วยเมธอด doSomething () และคลาสย่อยที่ใช้เมธอด doSomething () เป็นหนึ่งในวัตถุประสงค์หลักของ OOP, ความหลากหลาย Object foo = จำนวนเต็มใหม่ ("1"); foo.toString () ส่งกลับ 1 แม้ว่าจะถูกกำหนดให้กับ Object แต่ก็เป็นจำนวนเต็มดังนั้นจึงทำงานเช่นนี้ การเรียกใช้เมธอด toString จะเรียกใช้การใช้งาน Integer ความหลากหลาย
George Xavier

0

แค่คิดว่าจะโพสต์สิ่งที่พบว่ามีประโยชน์และอาจเป็นไปได้สำหรับคนที่มีความต้องการคล้าย ๆ กัน

วิธีต่อไปนี้เป็นวิธีที่ฉันเขียนสำหรับแอปพลิเคชัน JavaFX ของฉันเพื่อหลีกเลี่ยงการแคสต์และหลีกเลี่ยงการเขียนถ้า object x อินสแตนซ์ของคำสั่ง object b ทุกครั้งที่ส่งคืนคอนโทรลเลอร์

public <U> Optional<U> getController(Class<U> castKlazz){
    try {
        return Optional.of(fxmlLoader.<U>getController());
    }catch (Exception e){
        e.printStackTrace();
    }
    return Optional.empty();
}

การประกาศวิธีการขอรับคอนโทรลเลอร์คือ

public <T> T getController()

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

นี่คือลักษณะของการเรียกใช้เมธอดในขั้นสุดท้าย (หากมีอยู่ของอ็อบเจ็กต์ทางเลือกที่ส่งคืนจะใช้ Consumer

getController(LoadController.class).ifPresent(controller->controller.onNotifyComplete());

0

ลองใช้สิ่งนี้สำหรับการหล่อแบบไดนามิก มันจะทำงาน!!!

    String something = "1234";
    String theType = "java.lang.Integer";
    Class<?> theClass = Class.forName(theType);
    Constructor<?> cons = theClass.getConstructor(String.class);
    Object ob =  cons.newInstance(something);
    System.out.println(ob.equals(1234));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.