তালিকাটি <ডগ> তালিকার একটি সাবক্লাস <অনিমাল>? জাভা জেনেরিকগুলি কেন স্পষ্টতই বহুত্বপূর্ণ নয়?


769

আমি জাভা জেনারিকস কীভাবে উত্তরাধিকার / বহুমুখিতা পরিচালনা করে তা সম্পর্কে কিছুটা বিভ্রান্ত

নিম্নলিখিত শ্রেণিবিন্যাস অনুমান করুন -

প্রাণী (পিতামাতা)

কুকুর - বিড়াল (শিশু)

সুতরাং ধরুন আমার একটি পদ্ধতি আছে doSomething(List<Animal> animals)। সব সম্পত্তি ও পলিমরফিজম নিয়ম দ্বারা, আমি অনুমান করা হবে যে একটি List<Dog> হল একটি List<Animal>এবং একটি List<Cat> হল একটি List<Animal>- এবং তাই হয় এক এই পদ্ধতি প্রেরণ করা যেতে পারে। তাই না। আমি এই আচরণ অর্জন করতে চান তবে আমি স্পষ্টভাবে বলে প্রাণী কোন উপশ্রেণী একটি তালিকা গ্রহণ করতে পদ্ধতি জানাতে হবে doSomething(List<? extends Animal> animals)

আমি বুঝতে পারি এটি জাভার আচরণ। আমার প্রশ্ন কেন ? পলিমারফিজম সাধারণত অন্তর্নিহিত কেন, তবে জেনারিকের ক্ষেত্রে এটি নির্দিষ্ট করা আবশ্যক?


17
আর একটি সম্পূর্ণ সম্পর্কহীন ব্যাকরণ প্রশ্ন এখন আমাকে বিরক্ত করছে - আমার শিরোনাম "কেন হওয়া উচিত নয় জাভার জেনেরিক্স" বা "কেন নয় জাভার জেনেরিক্স" ?? "জেনেরিক্স" বহুগুণ কি s এর কারণে বা একক কারণে এটি একক সত্তা?
froadie

25
জাভা হিসাবে জেনেরিকগুলি প্যারামেট্রিক পলিমারফিজমের খুব দুর্বল রূপ। তাদের মধ্যে খুব বেশি বিশ্বাস স্থাপন করবেন না (যেমন আমি আগেও ব্যবহার করতাম), কারণ একদিন আপনি তাদের করুণাময় সীমাবদ্ধতার মারাত্মক আঘাত করবেন: সার্জন হ্যান্ডেবল <স্কেল্পেল>, হ্যান্ডেবল <স্পঞ্জ> কাবুমকে প্রসারিত করে! না না [এম] গনা। আপনার জাভা জেনেরিকস সীমাবদ্ধতা আছে। যে কোনও ওওএ / ওওডিকে জাভাতে সূক্ষ্মভাবে অনুবাদ করা যায় (এবং জাভা ইন্টারফেস ব্যবহার করে এমআই খুব সুন্দরভাবে করা যায়) তবে জেনেরিকগুলি কেবল এটি কাটবে না। তারা "সংগ্রহ" এবং পদ্ধতিগত প্রোগ্রামিংয়ের জন্য ঠিক আছে যা বলেছিল (যা বেশিরভাগ জাভা প্রোগ্রামাররা যাইহোক তাই করে ...)।
SyntaxT3rr0r

7
তালিকার সুপার ক্লাস <ডগ> তালিকা <অ্যানিমাল> নয় তবে তালিকা <?> (অর্থাত্ অজানা প্রকারের তালিকা)। জেনেরিক্স সংকলিত কোডে প্রকারের তথ্য মুছে দেয়। এটি করা হয়েছে যাতে জেনেরিকগুলি (জাভা 5 এবং উপরে) ব্যবহার করা কোডটি জেনেরিক ছাড়াই জাভার পূর্ববর্তী সংস্করণগুলির সাথে সামঞ্জস্যপূর্ণ।
rai.skumar


9
@ ফোরডি যেহেতু কেউ সাড়া দেয়নি বলে মনে হচ্ছে ... এটি অবশ্যই "জাভার জেনেরিক কেন হয় না ..." হওয়া উচিত। অন্য ইস্যুটি হ'ল "জেনেরিক" আসলে একটি বিশেষণ, এবং "জেনেরিকস" "জেনেরিক" দ্বারা সংশোধিত একটি বাদ পড়া বহুবচন বিশেষ্যকে উল্লেখ করছে। আপনি "সেই ফাংশনটি একটি জেনেরিক" বলতে পারেন তবে এটি "সেই ফাংশনটি জেনেরিক" বলার চেয়ে আরও জটিল হবে। তবে, "জাভা জেনেরিকস আছে" এর পরিবর্তে "জাভাতে জেনেরিক ফাংশন এবং ক্লাস রয়েছে" বলা কিছুটা জটিল umbers যে কেউ বিশেষণের উপর তাদের মাস্টারের থিসিস লিখেছেন বলে আমি মনে করি আপনি খুব আকর্ষণীয় প্রশ্নে হোঁচট খেয়েছেন!
ড্যান্টিস্টন

উত্তর:


915

না, একটি List<Dog>হল না একটি List<Animal>। আপনি একটি দিয়ে কী করতে পারেন তা বিবেচনা করুন List<Animal>- আপনি এটিতে কোনও প্রাণী যুক্ত করতে পারেন ... একটি বিড়াল সহ। এখন, আপনি কি যুক্তিসঙ্গতভাবে কুকুরছানাগুলির লিটারে একটি বিড়াল যুক্ত করতে পারেন? একেবারে না.

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

হঠাৎ আপনার একটি খুব বিভ্রান্ত বিড়াল আছে।

এখন, আপনি করতে পারবেন না একটি যোগ Catএকটি থেকে List<? extends Animal>কারণ আপনি এটি একটি জানি না List<Cat>। আপনি একটি মান পুনরুদ্ধার করতে পারেন এবং জানতে পারেন যে এটি একটি হবে Animalতবে আপনি নির্বিচারে প্রাণী যুক্ত করতে পারবেন না। বিপরীতটি সত্য List<? super Animal>- Animalএটির ক্ষেত্রে আপনি এটিতে নিরাপদে একটি যুক্ত করতে পারেন , তবে এটি থেকে কী উদ্ধার করা যেতে পারে সে সম্পর্কে আপনি কিছু জানেন না, কারণ এটি হতে পারে List<Object>


49
মজার ব্যাপার হচ্ছে, কুকুর প্রতিটি তালিকা হয় প্রকৃতপক্ষে পশুদের একটি তালিকা স্বজ্ঞা আমাদের বলে ঠিক। মুল বক্তব্যটি হ'ল, প্রাণীর প্রতিটি তালিকা কুকুরের তালিকা নয়, সুতরাং বিড়াল যুক্ত করে তালিকার পরিবর্তন করা সমস্যা is
ইঙ্গো

66
@ ইঙ্গো: না, সত্যিই নয়: আপনি প্রাণীদের তালিকায় একটি বিড়াল যুক্ত করতে পারেন, তবে আপনি কুকুরের তালিকায় একটি বিড়াল যুক্ত করতে পারবেন না। আপনি যদি কেবলমাত্র পঠনযোগ্য অর্থে বিবেচনা করেন তবে কুকুরের তালিকা কেবল প্রাণীর একটি তালিকা।
জন স্কিটি

12
@ জোনস্কিট - অবশ্যই, তবে কে হুকুম দিচ্ছে যে একটি বিড়াল থেকে নতুন তালিকা তৈরি করা এবং কুকুরের তালিকা আসলে কুকুরের তালিকা পরিবর্তন করে? এটি জাভাতে একটি নির্বিচার বাস্তবায়ন সিদ্ধান্ত implementation যুক্তি এবং স্বজ্ঞাততার সাথে পাল্টে যায় এমন একটি।
ইঙ্গো

7
@ ইঙ্গো: আমি এই "অবশ্যই" দিয়ে শুরু করতাম না। আপনার যদি এমন একটি তালিকা থাকে যা শীর্ষে "হোটেলগুলিতে যেতে চাই" এবং তারপরে কেউ এটিতে একটি সুইমিং পুল যুক্ত করেছে, আপনি কি এটিকে বৈধ মনে করবেন? না - এটি হোটেলগুলির একটি তালিকা, যা বিল্ডিংয়ের তালিকা নয়। এবং এটি এর মতো নয় এমনকি আমি বলেছিলাম "কুকুরের একটি তালিকা প্রাণীর তালিকা নয়" - আমি কোড কোডে , কোড ফন্টে রেখেছি । আমি সত্যিই মনে করি না যে এখানে কোনও অস্পষ্টতা আছে। সাবক্লাস ব্যবহার করা যাই হোক না কেন ভুল হবে - এটি বরাদ্দকরণের সামঞ্জস্যতার বিষয়ে, সাবক্লাসিংয়ের নয়।
জন স্কিটি

14
টুইট এবং আমি যুক্তি দিয়েছি যে অ্যারে কোভারিয়েন্সটি শুরু করার জন্য একটি ডিজাইনের ভুল ছিল।
জন স্কিটি

83

আপনি যা খুঁজছেন তাকে কোভারিয়েন্ট টাইপ পরামিতি বলে । এর অর্থ হ'ল যদি এক পদ্ধতিতে অন্য ধরণের অবজেক্টের পরিবর্তে প্রতিস্থাপন করা যায় (উদাহরণস্বরূপ, এর Animalসাথে প্রতিস্থাপন করা যায় Dog), একই জিনিসগুলি ব্যবহার করে এক্সপ্রেশনগুলিতে প্রয়োগ করা হয় (এর List<Animal>সাথে প্রতিস্থাপন করা যেতে পারে List<Dog>)। সমস্যাটি হল যে সাধারণভাবে পরিবর্তনীয় তালিকার জন্য সমবায় নিরাপদ নয়। মনে করুন আপনার একটি আছে List<Dog>এবং এটি একটি হিসাবে ব্যবহৃত হচ্ছে List<Animal>। যখন আপনি এই জন্য একটি বিড়াল যোগ করার জন্য চেষ্টা করি তাহলে কি হবে List<Animal>যা সত্যিই List<Dog>? টাইপ প্যারামিটারগুলিকে স্বয়ংক্রিয়ভাবে কোভারিয়েন্ট হিসাবে অনুমতি দেওয়া টাইপ সিস্টেমকে ব্রেক করে।

কোয়ারিয়ানেন্ট হিসাবে ধরণের পরামিতি নির্দিষ্ট করার জন্য সিনট্যাক্স যুক্ত করা কার্যকর হবে, যা ? extends Fooপদ্ধতি ঘোষণাকে এড়িয়ে চলে , তবে এতে অতিরিক্ত জটিলতা যুক্ত হয়।


44

কারণ একটি List<Dog>একটি নয় List<Animal>, যে, উদাহরণস্বরূপ, আপনি যদি একটি প্রবেশ করাতে পারে Catএকটি মধ্যে List<Animal>, কিন্তু একটি মধ্যে না List<Dog>... আপনি জেনেরিক্স আরো প্রসার্য করতে যেখানে সম্ভব ওয়াইল্ডকার্ড ব্যবহার করতে পারেন; উদাহরণস্বরূপ, একটি থেকে পড়া List<Dog>একই থেকে পড়ার অনুরূপ List<Animal>- তবে লেখা নয়।

জাভা ভাষা জেনেরিক্স এবং জাভা টিউটোরিয়াল থেকে জেনেরিক্স উপর অনুচ্ছেদ কেন কিছু জিনিস হিসেবে একটি খুব ভাল, ইন গভীরতার ব্যাখ্যা আছে বা বহুরুপী আসেনি বা জেনেরিক্স সঙ্গে অনুমতি।


36

আমি মনে করি এমন একটি বিষয় যা অন্যান্য উত্তরগুলির উল্লেখ করে তা যুক্ত করা উচিত while

List<Dog>isn't-একটি List<Animal> জাভা

এটাও সত্য

কুকুরের তালিকা হ'ল ইংরেজিতে প্রাণীদের একটি তালিকা (ভাল, একটি যুক্তিসঙ্গত ব্যাখ্যার অধীনে)

ওপি'র অন্তর্দৃষ্টিটি যেভাবে কাজ করে - যা সম্পূর্ণরূপে বৈধ - এটি পরবর্তী বাক্য। তবে, আমরা যদি এই স্বজ্ঞাতটিকে প্রয়োগ করি তবে আমরা এমন একটি ভাষা পাই যা এটির টাইপ সিস্টেমে জাভা-এস্কো নয়: মনে করুন আমাদের ভাষা কুকুরের তালিকায় একটি বিড়াল যুক্ত করার অনুমতি দেয়। তার মানে কী? এর অর্থ হ'ল তালিকাগুলি কুকুরের তালিকা হতে বন্ধ করে দেয় এবং এটি কেবলমাত্র পশুর একটি তালিকা থেকে যায়। এবং স্তন্যপায়ী প্রাণীর একটি তালিকা এবং চতুর্ভুজগুলির একটি তালিকা।

এটি অন্য উপায়ে দেওয়া: List<Dog>জাভাতে এ এর অর্থ ইংরেজীতে "কুকুরের তালিকা" নয়, এর অর্থ "কুকুর থাকতে পারে এমন একটি তালিকা এবং অন্য কিছুই নয়" means

আরও সাধারণভাবে, ও.পি. এর স্বজ্ঞাত নিজেকে এমন একটি ভাষার দিকে ধার দেয় যেখানে বস্তুর ক্রিয়াকলাপগুলি তাদের ধরণের পরিবর্তন করতে পারে বা বরং কোনও বস্তুর প্রকার (গুলি) এর মানটির একটি (গতিশীল) ফাংশন।


হ্যাঁ, মানুষের ভাষা আরও अस्पष्ट। তবুও, একবার আপনি কুকুরের তালিকায় একটি অন্য প্রাণী যুক্ত করলে, এটি এখনও পশুর একটি তালিকা, তবে কুকুরের তালিকা আর থাকবে না। পার্থক্য, অস্পষ্ট যুক্তিযুক্ত একটি মানুষ, সাধারণত এটি বুঝতে সমস্যা হয় না।
Vlasec

যেহেতু এমন কেউ যিনি অ্যারেগুলির সাথে ধ্রুবক তুলনাটি আরও বিভ্রান্ত করেন, এই উত্তরটি আমার কাছে তা পেরেছিল। আমার সমস্যা ছিল ভাষা স্বজ্ঞাত।
এফএলনলন

32

আমি বলব জেনারিক্সের পুরো বিষয়টিটি এটি এটির অনুমতি দেয় না। অ্যারেগুলির সাথে পরিস্থিতিটি বিবেচনা করুন, যা এই ধরণের সম্মিলনের অনুমতি দেয়:

  Object[] objects = new String[10];
  objects[0] = Boolean.FALSE;

এই কোডটি সূক্ষ্ম সংকলন করে, তবে রানটাইম ত্রুটি ছুঁড়ে দেয় ( java.lang.ArrayStoreException: java.lang.Booleanদ্বিতীয় লাইনে)। এটি টাইপসেফ নয়। জেনারিক্সের বিষয়টি হ'ল সংকলন টাইম ধরণের সুরক্ষা যুক্ত করা, অন্যথায় আপনি জেনারিকগুলি ছাড়া কেবল একটি সাধারণ শ্রেণীর সাথে থাকতে পারেন।

এখন এমন সময় রয়েছে যখন আপনার আরও নমনীয় হতে হবে এবং এটিই ? super Classএবং ? extends Classএর জন্য। পূর্ববর্তীটি যখন আপনাকে কোনও প্রকারের মধ্যে সন্নিবেশ করা প্রয়োজন Collection(উদাহরণস্বরূপ), এবং দ্বিতীয়টি যখন আপনি এটি থেকে পড়তে হবে তখন কোনও প্রকার সুরক্ষিত পদ্ধতিতে। তবে একই সাথে উভয়কেই করার একমাত্র উপায় হ'ল একটি নির্দিষ্ট ধরণের।


13
তর্কসাপেক্ষভাবে, অ্যারে কোভারিয়েন্স একটি ভাষা নকশা বাগ g নোট করুন যে মুছে যাওয়া টাইপ করার কারণে, জেনেরিক সংগ্রহের জন্য একই আচরণ প্রযুক্তিগতভাবে অসম্ভব।
মাইকেল বর্গওয়ার্ট

" আমি বলব জেনারিক্সের পুরো বিষয়টিটি এটি অনুমতি দেয় না "। আপনি কখনই নিশ্চিত হতে পারবেন না: জাভা এবং স্কালার টাইপ সিস্টেমগুলি আনসাউন্ড: নাল পয়েন্টারগুলির অস্তিত্বের সংকট (ওওপিএসএল 2016 তে উপস্থাপিত) (যেহেতু এটি সংশোধন করা হয়েছে)
ডেভিড টনহোফার

15

সমস্যাটি বোঝার জন্য অ্যারের সাথে তুলনা করা দরকারী।

List<Dog>এর সাবক্লাস নয়List<Animal>
কিন্তু Dog[] হয় এর উপশ্রেণী Animal[]

অ্যারেগুলি পুনরায় সংশোধনযোগ্য এবং কোভেরিয়েন্ট
পুনরুদ্ধারযোগ্য অর্থ তাদের টাইপ তথ্য রানটাইম এ সম্পূর্ণ উপলব্ধ available
সুতরাং অ্যারেগুলি রানটাইম ধরণের সুরক্ষা সরবরাহ করে তবে সংকলন-টাইপ সুরক্ষা দেয় না।

    // All compiles but throws ArrayStoreException at runtime at last line
    Dog[] dogs = new Dog[10];
    Animal[] animals = dogs; // compiles
    animals[0] = new Cat(); // throws ArrayStoreException at runtime

জেনেরিকের জন্য এটি বিপরীত:
জেনেরিকগুলি মুছে ফেলা হয় এবং অবিস্মরণীয় হয়
অতএব জেনেরিকগুলি রানটাইম ধরণের সুরক্ষা সরবরাহ করতে পারে না তবে তারা সংকলন-সময় ধরণের সুরক্ষা সরবরাহ করে।
নীচের কোডটিতে জেনেরিকরা যদি সমাগম হয় তবে 3 নং লাইনে গাদা দূষণ করা সম্ভব হবে ।

    List<Dog> dogs = new ArrayList<>();
    List<Animal> animals = dogs; // compile-time error, otherwise heap pollution
    animals.add(new Cat());

2
এটি যুক্তিযুক্ত হতে পারে যে, ঠিক এই কারণে, জাভাতে অ্যারেগুলি ভেঙে গেছে ,
লিওনব্লাই

অ্যারেগুলি কোভারিয়েন্ট হচ্ছে একটি সংকলক "বৈশিষ্ট্য"।
ক্রিশটিক

6

এখানে দেওয়া উত্তরগুলি পুরোপুরি আমাকে বোঝায় নি। পরিবর্তে, আমি অন্য একটি উদাহরণ তৈরি।

public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
    consumer.accept(supplier.get());
}

ভাল লাগছে, তাই না? তবে আপনি কেবল Consumerএস এবং Supplierএস এর জন্য পাস করতে পারবেন Animal। আপনার যদি কোনও Mammalভোক্তা, তবে Duckসরবরাহকারী থাকে তবে উভয়ই প্রাণী হলেও তাদের মাপসই করা উচিত নয়। এটি অস্বীকার করার জন্য, অতিরিক্ত বিধিনিষেধ যুক্ত করা হয়েছে।

উপরের পরিবর্তে, আমাদের ব্যবহার করা ধরণেরগুলির মধ্যে আমাদের সম্পর্ক নির্ধারণ করতে হবে।

ই। জি।,

public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
    consumer.accept(supplier.get());
}

নিশ্চিত করে যে আমরা কেবলমাত্র সরবরাহকারী ব্যবহার করতে পারি যা গ্রাহকের জন্য আমাদের সঠিক ধরণের অবজেক্ট সরবরাহ করে।

OTOH, আমরা পাশাপাশি করতে পারে

public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
    consumer.accept(supplier.get());
}

যেখানে আমরা অন্য পথে যাই: আমরা প্রকারের সংজ্ঞাটি দিয়ে থাকি Supplierএবং এটি সীমাবদ্ধ করে রাখি যে এটিতে রাখা যেতে পারে Consumer

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

public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
    consumer.accept(supplier.get());
}

যেখানে, স্বজ্ঞাত সম্পর্ক থাকার Life-> Animal-> Mammal-> Dog, Catইত্যাদি, এমনকি আমরা একটি করা হতে পারে Mammalএকটি মধ্যে Lifeভোক্তা, কিন্তু না একটি Stringএকটি মধ্যে Lifeভোক্তা।


1
4 টি সংস্করণের মধ্যে, # 2 সম্ভবত ভুল। যেমন আমরা সঙ্গে এটি কল করতে পারবেন না (Consumer<Runnable>, Supplier<Dog>)যখন Dogউপপ্রকার হয়Animal & Runnable
ZhongYu

5

এই জাতীয় আচরণের জন্য ভিত্তি যুক্তি হ'ল Genericsধরণ ক্ষয় করার একটি প্রক্রিয়া অনুসরণ করুন। সুতরাং রান সময়ে আপনার কোনও উপায় নেই যেখানে ধরণের ক্ষয় করার প্রক্রিয়া নেই তার collectionবিপরীতে প্রকারের শনাক্ত করে arrays। সুতরাং আপনার প্রশ্নে ফিরে আসছি ...

সুতরাং ধরুন নীচের মত একটি পদ্ধতি রয়েছে:

add(List<Animal>){
    //You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
}

এখন যদি জাভা এই পদ্ধতিতে কলারকে প্রকারের তালিকার তালিকা যুক্ত করতে দেয় তবে আপনি সংগ্রহের ক্ষেত্রে ভুল জিনিস যুক্ত করতে পারেন এবং রান সময়েও এটি টাইপ ইরেজরের কারণে চলবে। অ্যারেগুলির ক্ষেত্রে আপনি এই জাতীয় দৃশ্যের জন্য একটি রান সময় ব্যতিক্রম পাবেন ...

সুতরাং সংক্ষেপে এই আচরণটি এমনভাবে প্রয়োগ করা হয় যাতে কেউ কোনও ভুল জিনিস সংগ্রহ করতে না পারে। এখন আমি বিশ্বাস করি প্রকার মুছে ফেলা তাই জেনারিক ছাড়াই উত্তরাধিকারী জাভার সাথে সামঞ্জস্যতা দিতে ....


4

Subtyping হয় পরিবর্তিত স্থিতিমাপ ধরনের জন্য। এমনকি শক্ত শ্রেণীর Dogএকটি উপপ্রকার Animal, পরামিতিযুক্ত ধরণের List<Dog>একটি উপপ্রকার নয় List<Animal>। এর বিপরীতে, covariant subtyping, অ্যারে দ্বারা ব্যবহৃত তাই অ্যারের টাইপ করা হয় Dog[]একটি উপপ্রকার হয় Animal[]

ইনভেআরেন্ট সাব টাইপিং নিশ্চিত করে যে জাভা দ্বারা প্রয়োগ করা ধরণের সীমাবদ্ধতা লঙ্ঘন করা হয়নি। @ জন স্কিটির প্রদত্ত নিম্নলিখিত কোডটি বিবেচনা করুন:

List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);

@ জোন স্কিটের বক্তব্য অনুসারে, এই কোডটি অবৈধ, কারণ অন্যথায় এটি কুকুরের প্রত্যাশার সাথে বিড়াল ফিরিয়ে দেওয়ার ধরণের সীমাবদ্ধতা লঙ্ঘন করবে।

অ্যারেগুলির জন্য অনুরূপ কোডের সাথে উপরেরটির তুলনা করা শিক্ষণীয়।

Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];

কোডটি বৈধ। তবে, একটি ছোঁড়ার অ্যারে দোকান ব্যতিক্রম । একটি অ্যারে রান টাইমে এই ধরণের বহন করে এভাবে JVM কোভেরিয়েন্ট সাব টাইপিংয়ের ধরণের সুরক্ষা প্রয়োগ করতে পারে।

এটি আরও বুঝতে, আসুন javapনীচের শ্রেণীর দ্বারা উত্পন্ন বাইটকোডটি দেখুন:

import java.util.ArrayList;
import java.util.List;

public class Demonstration {
    public void normal() {
        List normal = new ArrayList(1);
        normal.add("lorem ipsum");
    }

    public void parameterized() {
        List<String> parameterized = new ArrayList<>(1);
        parameterized.add("lorem ipsum");
    }
}

কমান্ডটি ব্যবহার করে javap -c Demonstrationএটি নিম্নলিখিত জাভা বাইটকোডটি দেখায়:

Compiled from "Demonstration.java"
public class Demonstration {
  public Demonstration();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void normal();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: iconst_1
       5: invokespecial #3                  // Method java/util/ArrayList."<init>":(I)V
       8: astore_1
       9: aload_1
      10: ldc           #4                  // String lorem ipsum
      12: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      17: pop
      18: return

  public void parameterized();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: iconst_1
       5: invokespecial #3                  // Method java/util/ArrayList."<init>":(I)V
       8: astore_1
       9: aload_1
      10: ldc           #4                  // String lorem ipsum
      12: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      17: pop
      18: return
}

লক্ষ্য করুন যে পদ্ধতিগুলির সংস্থাগুলির অনুবাদিত কোডটি অভিন্ন। সংকলক প্রতিটি প্যারামিটারাইজড টাইপটিকে এর ক্ষয় দ্বারা প্রতিস্থাপন করে । এই সম্পত্তিটি গুরুত্বপূর্ণ অর্থ যে এটি পিছনে সামঞ্জস্যতা ভাঙ্গেনি।

উপসংহারে, প্যারামিটারযুক্ত ধরণের জন্য রান-টাইম সুরক্ষা সম্ভব নয়, যেহেতু সংকলক প্রতিটি প্যারামিটারাইজড টাইপটিকে তার ক্ষয় দ্বারা প্রতিস্থাপন করে। এটি প্যারামিটারাইজড প্রকারগুলি সিনট্যাকটিক চিনির চেয়ে বেশি কিছু নয়।


3

আসলে আপনি যা চান তা অর্জন করতে আপনি একটি ইন্টারফেস ব্যবহার করতে পারেন।

public interface Animal {
    String getName();
    String getVoice();
}
public class Dog implements Animal{
    @Override 
    String getName(){return "Dog";}
    @Override
    String getVoice(){return "woof!";}

}

তারপরে আপনি সংগ্রহগুলি ব্যবহার করে ব্যবহার করতে পারেন

List <Animal> animalGroup = new ArrayList<Animal>();
animalGroup.add(new Dog());

1

আপনি যদি নিশ্চিত হন যে তালিকার আইটেমগুলি সেই প্রদত্ত সুপার টাইপের সাবক্লাস হয় তবে আপনি এই পদ্ধতির সাহায্যে তালিকাটি কাস্ট করতে পারেন:

(List<Animal>) (List<?>) dogs

আপনি যখন কোনও কনস্ট্রাক্টরের তালিকাটি পাস করতে চান বা তার উপরে পুনরাবৃত্তি করতে চান এটি এটি দরকারী


2
এটি এটির সমাধানের চেয়ে আরও সমস্যা তৈরি করবে
ফেরিবিগ

আপনি যদি তালিকায় একটি বিড়াল যুক্ত করার চেষ্টা করেন তবে নিশ্চিত করুন এটি সমস্যার সৃষ্টি করবে, তবে লুপিংয়ের উদ্দেশ্যে আমি মনে করি এটির একমাত্র অবিশ্বাস্য উত্তর।

1

উত্তর পাশাপাশি অন্যান্য উত্তর সঠিক। আমি সেই উত্তরগুলিতে এমন একটি সমাধান যুক্ত করতে যাচ্ছি যা আমি মনে করি যে এটি সহায়ক হবে। আমি মনে করি এটি প্রায়শই প্রোগ্রামিংয়ে আসে। একটি বিষয় লক্ষণীয় যে সংগ্রহগুলির জন্য (তালিকাগুলি, সেটগুলি, ইত্যাদি) মূল সমস্যাটি সংযোজনে যুক্ত হচ্ছে। সেখানেই জিনিসগুলি ভেঙে যায়। এমনকি অপসারণ ঠিক আছে।

বেশিরভাগ ক্ষেত্রে, আমরা তার Collection<? extends T>পরিবর্তে ব্যবহার করতে পারি Collection<T>এবং এটি প্রথম পছন্দ হওয়া উচিত। যাইহোক, আমি এমন কেসগুলি সন্ধান করছি যেখানে এটি করা সহজ নয়। এটি সর্বদা সবচেয়ে ভাল জিনিস কিনা তা নিয়ে বিতর্কের অবতারণা। আমি এখানে একটি বর্গ DownCastCollection যে রূপান্তর একটি গ্রহণ করতে পারেন উপস্থাপন করছি Collection<? extends T>একটি থেকে Collection<T>(আমরা তালিকা, সেট, NavigableSet, .. জন্য অনুরূপ শ্রেণী বর্ণনা করতে পারেন) যখন মান পদ্ধতির ব্যবহার ব্যবহার করা খুব অসুবিধাজনক। নীচে এটি কীভাবে ব্যবহার করা যায় তার একটি উদাহরণ দেওয়া আছে (আমরা Collection<? extends Object>এই ক্ষেত্রেও ব্যবহার করতে পারতাম , তবে ডাউনকাস্টক্লেকশন ব্যবহার করে চিত্রিত করার জন্য আমি এটিকে সহজ রাখছি।

/**Could use Collection<? extends Object> and that is the better choice. 
* But I am doing this to illustrate how to use DownCastCollection. **/

public static void print(Collection<Object> col){  
    for(Object obj : col){
    System.out.println(obj);
    }
}
public static void main(String[] args){
  ArrayList<String> list = new ArrayList<>();
  list.addAll(Arrays.asList("a","b","c"));
  print(new DownCastCollection<Object>(list));
}

এখন ক্লাস:

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
private Collection<? extends E> delegate;

public DownCastCollection(Collection<? extends E> delegate) {
    super();
    this.delegate = delegate;
}

@Override
public int size() {
    return delegate ==null ? 0 : delegate.size();
}

@Override
public boolean isEmpty() {
    return delegate==null || delegate.isEmpty();
}

@Override
public boolean contains(Object o) {
    if(isEmpty()) return false;
    return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
    Iterator<? extends E> delegateIterator;

    protected MyIterator() {
        super();
        this.delegateIterator = delegate == null ? null :delegate.iterator();
    }

    @Override
    public boolean hasNext() {
        return delegateIterator != null && delegateIterator.hasNext();
    }

    @Override
    public  E next() {
        if(!hasNext()) throw new NoSuchElementException("The iterator is empty");
        return delegateIterator.next();
    }

    @Override
    public void remove() {
        delegateIterator.remove();

    }

}
@Override
public Iterator<E> iterator() {
    return new MyIterator();
}



@Override
public boolean add(E e) {
    throw new UnsupportedOperationException();
}

@Override
public boolean remove(Object o) {
    if(delegate == null) return false;
    return delegate.remove(o);
}

@Override
public boolean containsAll(Collection<?> c) {
    if(delegate==null) return false;
    return delegate.containsAll(c);
}

@Override
public boolean addAll(Collection<? extends E> c) {
    throw new UnsupportedOperationException();
}

@Override
public boolean removeAll(Collection<?> c) {
    if(delegate == null) return false;
    return delegate.removeAll(c);
}

@Override
public boolean retainAll(Collection<?> c) {
    if(delegate == null) return false;
    return delegate.retainAll(c);
}

@Override
public void clear() {
    if(delegate == null) return;
        delegate.clear();

}

}


এটি একটি ভাল ধারণা, এটি ইতিমধ্যে জাভা এসইতে বিদ্যমান in ; )Collections.unmodifiableCollection
রেডিওডেফ

1
ঠিক আছে তবে আমি সংজ্ঞায়িত সংগ্রহটি সংশোধন করা যেতে পারে।
ডান বি

হ্যাঁ, এটি পরিবর্তন করা যেতে পারে। Collection<? extends E>ইতিমধ্যে সেই আচরণটি ইতিমধ্যে সঠিকভাবে পরিচালনা করে যদি না আপনি এটি এমনভাবে ব্যবহার না করেন যা টাইপ-নিরাপদ নয় (যেমন এটি অন্য কোনও ক্ষেত্রে ingালাই)। আমি কেবল সেখানেই দেখছি যখন আপনি addঅপারেশনটি কল করেন , আপনি এটি কাস্ট করলেও এটি ব্যতিক্রম ছুঁড়ে দেয়।
Vlasec

0

জাভাএসই টিউটোরিয়াল থেকে উদাহরণ নিতে দিন

public abstract class Shape {
    public abstract void draw(Canvas c);
}

public class Circle extends Shape {
    private int x, y, radius;
    public void draw(Canvas c) {
        ...
    }
}

public class Rectangle extends Shape {
    private int x, y, width, height;
    public void draw(Canvas c) {
        ...
    }
}

সুতরাং কুকুরের একটি তালিকা (চেনাশোনা) কেন স্পষ্টভাবে প্রাণীর একটি তালিকা (আকার) বিবেচনা করা উচিত নয় কারণ এই পরিস্থিতির কারণ:

// drawAll method call
drawAll(circleList);


public void drawAll(List<Shape> shapes) {
   shapes.add(new Rectangle());    
}

সুতরাং জাভা "স্থপতি" এর 2 টি বিকল্প ছিল যা এই সমস্যার সমাধান করে:

  1. সাব-টাইপটি স্পষ্টতই এটি সুপার টাইপের, এবং এখনকার মতো একটি সংকলন ত্রুটি দেয় তা বিবেচনা করবেন না

  2. সাব-টাইপটিকে এটি সুপারটাইপ হিসাবে বিবেচনা করুন এবং "অ্যাড" পদ্ধতিটি সংকলনে সীমাবদ্ধ রাখুন (সুতরাং আঁকার সমস্ত পদ্ধতিতে, চেনাশোনাগুলির একটি তালিকা, আকারের উপ-প্রকার, পাস করা হবে, সংকলকটি সনাক্ত করতে হবে এবং আপনাকে সংকলনের ত্রুটির সাথে সীমাবদ্ধ করে যে)।

সুস্পষ্ট কারণে, এটি প্রথম উপায়টি বেছে নিয়েছিল।


0

আমাদেরও বিবেচনায় নেওয়া উচিত যে সংকলকটি জেনেরিক ক্লাসগুলিকে কীভাবে হুমকি দেয়: যখনই আমরা জেনেরিক আর্গুমেন্টগুলি পূরণ করি একটি ভিন্ন ধরণের "ইনস্ট্যান্টিয়েট করে"।

সুতরাং আমরা আছে ListOfAnimal, ListOfDog, ListOfCat, ইত্যাদি, যা স্বতন্ত্র শ্রেণীর যে শেষ পর্যন্ত কম্পাইলার যখন আমরা জেনেরিক আর্গুমেন্ট উল্লেখ দ্বারা "তৈরি" হচ্ছে। এবং এটি একটি ফ্ল্যাট শ্রেণিবদ্ধতা (আসলে সম্পর্কিত Listকোনওভাবেই শ্রেণিবদ্ধ নয়)।

জেনেরিক ক্লাসগুলির ক্ষেত্রে কেন সমবায়তা বোঝায় না তা অন্য যুক্তি হ'ল বেসের ভিত্তিতে সমস্ত শ্রেণি একই - Listউদাহরণগুলি। Listজেনেরিক আর্গুমেন্ট পূরণ করে একটি বিশেষত্ব বর্গকে প্রসারিত করে না, এটি কেবল সেই নির্দিষ্ট জেনেরিক যুক্তির পক্ষে কাজ করে।


0

সমস্যাটি ভালভাবে চিহ্নিত করা হয়েছে। তবে একটি সমাধান আছে; করতে doSomething জেনেরিক:

<T extends Animal> void doSomething<List<T> animals) {
}

এখন আপনি তালিকা <ডগ> বা তালিকার <চ্যাট> বা তালিকা <অ্যানিমাল> এর সাথে ডোজ কিছুতে কল করতে পারেন।


0

আরেকটি সমাধান হ'ল নতুন তালিকা তৈরি করা

List<Dog> dogs = new ArrayList<Dog>(); 
List<Animal> animals = new ArrayList<Animal>(dogs);
animals.add(new Cat());

0

জন স্কিটির উত্তর, যা এই উদাহরণ কোডটি ব্যবহার করে:

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

গভীরতম স্তরে, এখানে সমস্যা হল dogsএবং animalsভাগ একটি রেফারেন্স। তার মানে এই যে এই কাজটি করার একটি উপায় হ'ল পুরো তালিকাটি অনুলিপি করা, যা রেফারেন্সের সাম্যতা ভঙ্গ করবে:

// This code is fine
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
List<Animal> animals = new ArrayList<>(dogs); // Copy list
animals.add(new Cat());
Dog dog = dogs.get(0);   // This is fine now, because it does not return the Cat

কলিং পর List<Animal> animals = new ArrayList<>(dogs);, আপনি পরবর্তীকালে সরাসরি ধার্য করতে পারেন না animalsউভয় dogsবা cats:

// These are both illegal
dogs = animals;
cats = animals;

অতএব আপনি তালিকার ভুল উপ টাইপটি রাখতে পারবেন না Animal, কারণ কোনও ভুল উপ-প্রকার নেই - উপ-টাইপের কোনও বস্তু ? extends Animalযুক্ত হতে পারে animals

স্পষ্টতই, এটি শব্দার্থবিজ্ঞানের পরিবর্তন করে, যেহেতু তালিকাগুলি animalsএবং dogsআর ভাগ করে নেওয়া হয় না, সুতরাং একটি তালিকায় যুক্ত করা অন্যটিতে যুক্ত হয় না (যা আপনি যা চান ঠিক তেমন সমস্যাটি এড়াতে Catযে কেবল একটি তালিকায় যুক্ত হতে পারে) Dogঅবজেক্টগুলি ধারণ করে)। এছাড়াও, পুরো তালিকাটি অনুলিপি করা অদক্ষ হতে পারে। যাইহোক, এটি রেফারেন্স সাম্যতা ভঙ্গ করে সমতুল্য ধরণের সমস্যাটিকে সমাধান করে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.