জেনেরিক রিটার্ন টাইপ আপার বাউন্ড - ইন্টারফেস বনাম শ্রেণি - আশ্চর্যজনকভাবে কার্যকর কোড


171

এটি একটি তৃতীয় পক্ষের লাইব্রেরি এপিআইয়ের বাস্তব-বিশ্বের উদাহরণ, তবে সরলীকৃত।

ওরাকল জেডিকে 8u72 দিয়ে সংকলিত

এই দুটি পদ্ধতি বিবেচনা করুন:

<X extends CharSequence> X getCharSequence() {
    return (X) "hello";
}

<X extends String> X getString() {
    return (X) "hello";
}

উভয়ই "চেক না করা castালাই" সতর্কতার প্রতিবেদন করে - আমি কেন পাই। যে বিষয়টি আমাকে বিভ্রান্ত করে তা হ'ল আমি কেন ফোন করতে পারি

Integer x = getCharSequence();

এবং এটি সংকলন? সংকলকটি জানতে হবে যা Integerবাস্তবায়ন করে না CharSequence। কল

Integer y = getString();

একটি ত্রুটি দেয় (যেমন প্রত্যাশিত)

incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String

কেউ ব্যাখ্যা করতে পারেন কেন এই আচরণটি বৈধ হিসাবে বিবেচিত হবে? এটা কিভাবে দরকারী হবে?

ক্লায়েন্ট জানেন না যে এই কলটি অনিরাপদ - ক্লায়েন্টের কোডটি সতর্কতা ছাড়াই সংকলন করে। সংকলন কেন সে সম্পর্কে সতর্ক করবে না / ত্রুটি জারি করবে?

এছাড়াও, এই উদাহরণ থেকে এটি কীভাবে আলাদা:

<X extends CharSequence> void doCharSequence(List<X> l) {
}

List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles

List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // error

পাস করার চেষ্টা List<Integer>একটি প্রত্যাশা হিসাবে ত্রুটি দেয়:

method doCharSequence in class generic.GenericTest cannot be applied to given types;
  required: java.util.List<X>
  found: java.util.List<java.lang.Integer>
  reason: inference variable X has incompatible bounds
    equality constraints: java.lang.Integer
    upper bounds: java.lang.CharSequence

যদি এটি ত্রুটি হিসাবে প্রতিবেদন করা Integer x = getCharSequence();হয় তবে কেন নয়?


15
মজাদার! এলএইচএসে compালাই Integer x = getCharSequence();সংকলন করবে, তবে আরএইচএসে Integer x = (Integer) getCharSequence();
ফ্লাক্স

আপনি জাভা সংকলকটির কোন সংস্করণ ব্যবহার করছেন? প্রশ্নে এই তথ্য নির্দিষ্ট করুন।
ফেডেরিকো পেরালটা শ্যাফনার

@ ফেডেরিকোপ্যারাল্টা শ্যাফনার এই বিষয়টি কেন বুঝতে পারে না - এটি জেএলএস সম্পর্কে সরাসরি প্রশ্ন about
বরিস স্পাইডার

@ বোরিস্টস্পাইডার কারণ জাভা 8-র জন্য টাইপ অনুমানের ব্যবস্থাটি পরিবর্তিত হয়েছে
ফেডেরিকো

1
@ ফেডেরিকোপ্যারালটা শ্যাফনার - আমি প্রশ্নটি ইতিমধ্যে [জাভা -8] এর সাথে ট্যাগ করেছি, তবে আমি এখন পোস্টে সংকলক সংস্করণ যুক্ত করেছি।
অ্যাডাম মিশালিক

উত্তর:


184

CharSequenceএকটি interface। সুতরাং SomeClassবাস্তবায়ন না হলেও CharSequenceএটি একটি শ্রেণি তৈরি করা পুরোপুরি সম্ভব হবে would

class SubClass extends SomeClass implements CharSequence

সুতরাং আপনি লিখতে পারেন

SomeClass c = getCharSequence();

কারণ অনুমান করা টাইপটি Xছেদ করার ধরণ SomeClass & CharSequence

এটি চূড়ান্ত Integerকারণ ক্ষেত্রে কিছুটা বিজোড় Integer, কিন্তু finalএই নিয়মে কোনও ভূমিকা পালন করে না। যেমন আপনি লিখতে পারেন

<T extends Integer & CharSequence>

অন্যদিকে, Stringএটি একটি নয় interface, সুতরাং এটির SomeClassএকটি সাব-টাইপ পেতে প্রসারিত করা অসম্ভব হবে String, কারণ জাভা ক্লাসগুলির একাধিক-উত্তরাধিকার সমর্থন করে না।

সঙ্গে Listউদাহরণস্বরূপ, আপনার মনে রাখতে যে জেনেরিক্স তন্ন তন্ন covariant কিংবা contravariant হয় প্রয়োজন। এর অর্থ এই যে যদি Xএকটি উপপ্রকার হয় Y, List<X>তন্ন তন্ন একটি উপপ্রকার কিংবা একটি supertype হয় List<Y>। যেহেতু Integerবাস্তবায়ন হয় না CharSequence, আপনি List<Integer>আপনার doCharSequenceপদ্ধতিতে ব্যবহার করতে পারবেন না ।

আপনি তবে এটি সংকলন করতে পারেন

<T extends Integer & CharSequence> void foo(List<T> list) {
    doCharSequence(list);
}  

আপনি যদি একটি পদ্ধতি যা থাকে ফেরৎ একটি List<T>ভালো:

static <T extends CharSequence> List<T> foo() 

আপনি করতে পারেন

List<? extends Integer> list = foo();

আবার, এটি কারণ ইনফার্ডেড টাইপ Integer & CharSequenceএবং এটি একটি সাব টাইপ Integer

ছেদ করার ধরণগুলি স্পষ্টভাবে ঘটে যখন আপনি একাধিক সীমা নির্দিষ্ট করে (যেমন <T extends SomeClass & CharSequence>)।

আরও তথ্যের জন্য, এখানে জেএলএস এর অংশ যেখানে এটি ব্যাখ্যা করে যে টাইপ সীমা কীভাবে কাজ করে। আপনি একাধিক ইন্টারফেস অন্তর্ভুক্ত করতে পারেন, যেমন

<T extends String & CharSequence & List & Comparator>

তবে কেবল প্রথম গণ্ডিটি একটি অ ইন্টারফেস হতে পারে।


62
আপনি &জেনেরিক সংজ্ঞা দিতে পারেন তা আমার কোনও ধারণা ছিল না । +1 টি
থাক

13
@ ফ্লাক্স আপনি একাধিক রাখতে পারেন তবে কেবল প্রথম যুক্তিটি একটি নন-ইন্টারফেস হতে পারে। <T extends String & List & Comparator>ঠিক আছে তবে <T extends String & Integer>তা নয়, কারণ Integerএটি কোনও ইন্টারফেস নয়।
পল বোডিংটন

7
@ পলবডিংটন এই পদ্ধতিগুলির জন্য কিছু ব্যবহারিক ব্যবহার রয়েছে। উদাহরণস্বরূপ যদি টাইপটি আসলে সঞ্চয় করা ডেটার জন্য ব্যবহার না করা হয়। এই জন্য উদাহরণ হল Collections.emptyList()সেইসাথে Optional.empty()। জেনেরিক ইন্টারফেসের এই রিটার্ন বাস্তবায়ন, তবে কিছু সঞ্চয় করে না।
স্টিফান ডোলাস

6
এবং কেউই বলে না যে একটি শ্রেণি finalসংকলন-সময়ে finalথাকা রানটাইম সময়ে হবে ।
হোলার

7
@ ফেডেরিকো পেরালটা শ্যাফনার: এখানে মূল বক্তব্যটি হ'ল, পদ্ধতিটি কলারের getCharSequence()যা Xপ্রয়োজন তা ফেরত দেওয়ার প্রতিশ্রুতি দেয় , এর মধ্যে রয়েছে কলারের প্রয়োজন মতো এক প্রকারের প্রসারিত করা Integerএবং বাস্তবায়ন CharSequenceকরা এবং এই প্রতিশ্রুতি অনুসারে, ফলাফল নির্ধারণের অনুমতি দেওয়া সঠিক Integer। এটি সেই পদ্ধতি getCharSequence()যা ভেঙে গেছে কারণ এটি তার প্রতিশ্রুতি রাখে না, তবে এটি সংকলকের ত্রুটি নয়।
হলগার

59

অ্যাসাইনমেন্টের আগে আপনার সংকলক দ্বারা যে ধরণের অনুমান করা হয় তা Xহ'ল Integer & CharSequence। এই ধরণেরটি অদ্ভুত বোধ করে , কারণ Integerএটি চূড়ান্ত তবে জাভাতে এটি পুরোপুরি বৈধ টাইপ। এটি তখন নিক্ষেপ করা হয় Integer, যা পুরোপুরি ঠিক আছে।

সেখানে ঠিক একটি সম্ভাব্য মান Integer & CharSequenceটাইপ: null। নিম্নলিখিত বাস্তবায়ন সহ:

<X extends CharSequence> X getCharSequence() {
    return null;
}

নিম্নলিখিত অ্যাসাইনমেন্টটি কাজ করবে:

Integer x = getCharSequence();

এই সম্ভাব্য মানটির কারণে, স্পষ্টতই অকেজো হওয়া সত্ত্বেও, অ্যাসাইনমেন্টটি ভুল হওয়ার কোনও কারণ নেই। একটি সতর্কতা দরকারী হবে।

আসল সমস্যাটি এপিআই, কল সাইট নয়

আসলে, আমি সম্প্রতি এই এপিআই ডিজাইন বিরোধী প্যাটার্ন সম্পর্কে ব্লগ করেছি । আপনার (প্রায়) কখনই স্বেচ্ছাচারী প্রকারগুলি ফিরিয়ে আনার জন্য জেনেরিক পদ্ধতিটি ডিজাইন করা উচিত নয় কারণ আপনি (প্রায়) কখনই গ্যারান্টি দিতে পারবেন না যে অনুমানকৃত ধরণের সরবরাহ করা হবে। ব্যতিক্রমগুলির মতো পদ্ধতিগুলি যেমন Collections.emptyList()তালিকার শূন্যতা (এবং জেনেরিক ধরণের ক্ষয়) এর কারণগুলির জন্য কোনও অনুমান <T>কাজ করবে তার কারণ :

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.