আমি জাভা কোড জুড়ে এসেছি:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
উপরোক্ত তিনটির মধ্যে পার্থক্য কী এবং তারা জাভাতে এই ধরণের শ্রেণি বা ইন্টারফেসের ঘোষণাকে কী বলে?
আমি জাভা কোড জুড়ে এসেছি:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
উপরোক্ত তিনটির মধ্যে পার্থক্য কী এবং তারা জাভাতে এই ধরণের শ্রেণি বা ইন্টারফেসের ঘোষণাকে কী বলে?
উত্তর:
ঠিক আছে প্রথম দুটির মধ্যে কোনও পার্থক্য নেই - তারা কেবলমাত্র পরামিতি ( E
বা T
) টাইপের জন্য আলাদা আলাদা নাম ব্যবহার করছে ।
তৃতীয়টি বৈধ ঘোষণা নয় - ?
একটি ওয়াইল্ডকার্ড হিসাবে ব্যবহৃত হয় যা কোনও প্রকারের যুক্তি সরবরাহ করার সময় ব্যবহৃত হয় , উদাহরণস্বরূপ এর List<?> foo = ...
অর্থ এটি foo
কোনও ধরণের একটি তালিকা বোঝায়, তবে আমরা কী জানি না।
এই সমস্ত জেনেরিক , যা একটি দুর্দান্ত বিশাল বিষয়। আপনি নিম্নলিখিত উত্সগুলির মাধ্যমে এটি সম্পর্কে জানতে ইচ্ছুক হতে পারেন, যদিও আরও অবশ্যই পাওয়া যায়:
T
এবং E
- তারা কেবল শনাক্তকারী। আপনি KeyValuePair<K, V>
উদাহরণস্বরূপ লিখতে পারে । ?
যদিও এর বিশেষ অর্থ রয়েছে।
এটি অন্য যে কোনও কিছুর চেয়ে বেশি কনভেনশন।
T
একটি টাইপ হতে বোঝানো হয় E
একটি উপাদান হিসাবে বোঝানো হয় ( List<E>
: উপাদানগুলির একটি তালিকা) K
কী (একটিতে Map<K,V>
) V
মান (ফেরতের মান বা ম্যাপযুক্ত মান হিসাবে) তারা সম্পূর্ণরূপে বিনিময়যোগ্য (তবুও একই ঘোষণায় দ্বন্দ্ব)।
পূর্ববর্তী উত্তরগুলি টাইপ প্যারামিটারগুলি (টি, ই, ইত্যাদি) ব্যাখ্যা করে তবে ওয়াইল্ডকার্ড, "?" বা তাদের মধ্যে পার্থক্য ব্যাখ্যা করে না, তাই আমি এটিকে সম্বোধন করব।
প্রথমত, পরিষ্কার করার জন্য: ওয়াইল্ডকার্ড এবং প্রকারের প্যারামিটারগুলি এক নয়। যেখানে টাইপ প্যারামিটারগুলি এক ধরণের ভেরিয়েবলের (উদাহরণস্বরূপ, টি) সংজ্ঞা দেয় যা কোনও স্কোপের জন্য প্রকারের প্রতিনিধিত্ব করে, ওয়াইল্ডকার্ড এটি দেয় না: ওয়াইল্ডকার্ড কেবলমাত্র জেনেরিক টাইপের জন্য ব্যবহারযোগ্য অনুমতিযোগ্য ধরণের একটি সেট নির্ধারণ করে। কোনও বাউন্ডিং ( extends
বা super
) ছাড়াই ওয়াইল্ডকার্ডের অর্থ "এখানে যে কোনও ধরণের ব্যবহার করুন"।
ওয়াইল্ডকার্ড সবসময় কোণ বন্ধনীগুলির মধ্যে আসে এবং এটি কেবল জেনেরিক ধরণের প্রসঙ্গে থাকে:
public void foo(List<?> listOfAnyType) {...} // pass a List of any type
না
public <?> ? bar(? someType) {...} // error. Must use type params here
অথবা
public class MyGeneric ? { // error
public ? getFoo() { ... } // error
...
}
এটি আরও বিভ্রান্ত হয় যেখানে তারা ওভারল্যাপ করে। উদাহরণ স্বরূপ:
List<T> fooList; // A list which will be of type T, when T is chosen.
// Requires T was defined above in this scope
List<?> barList; // A list of some type, decided elsewhere. You can do
// this anywhere, no T required.
পদ্ধতির সংজ্ঞা দিয়ে কী সম্ভব তা নিয়ে প্রচুর ওভারল্যাপ রয়েছে। নিম্নলিখিতটি ক্রিয়ামূলকভাবে অভিন্ন:
public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething) {...}
সুতরাং, যদি ওভারল্যাপ থাকে তবে কেন একটি বা অন্যটি ব্যবহার করবেন? কখনও কখনও, এটি সত্যই সরল স্টাইল: কিছু লোক বলে যে আপনার যদি টাইপ পরমের প্রয়োজন না হয় তবে কোডটি আরও সহজ / আরও পঠনযোগ্য করার জন্য আপনার ওয়াইল্ডকার্ড ব্যবহার করা উচিত। একটি মূল পার্থক্য যা আমি উপরে ব্যাখ্যা করেছি: টাইপ প্যারামগুলি টাইপ ভেরিয়েবল (উদাহরণস্বরূপ, টি) সংজ্ঞায়িত করে যা আপনি অন্য কোথাও স্কোপে ব্যবহার করতে পারবেন; ওয়াইল্ডকার্ড না। অন্যথায়, টাইপ প্যারাম এবং ওয়াইল্ডকার্ডের মধ্যে দুটি বড় পার্থক্য রয়েছে:
টাইপ প্যারামে একাধিক বাউন্ডিং ক্লাস থাকতে পারে; ওয়াইল্ডকার্ড পারবেন না:
public class Foo <T extends Comparable<T> & Cloneable> {...}
ওয়াইল্ডকার্ডের নিম্ন সীমানা থাকতে পারে; টাইপ প্যারামগুলি:
public void bar(List<? super Integer> list) {...}
উপরের দিকে ওয়াইল্ডকার্ডের উপর একটি নিম্ন সীমা হিসাবে List<? super Integer>
সংজ্ঞায়িত Integer
করা হয়েছে, যার অর্থ তালিকার ধরণটি অবশ্যই পূর্ণসংখ্যার বা একটি সুপার টাইপের পূর্ণসংখ্যার হতে হবে। জেনেরিক টাইপ বাউন্ডিং আমি বিস্তারিত কভার করতে চাই তার বাইরে। সংক্ষেপে, এটা আপনি সংজ্ঞায়িত করতে পারবেন কোন ধরনের একটি জেনেরিক টাইপ হতে পারে। এটি জিনেরিকগুলি বহুবিকভাবে চিকিত্সা করা সম্ভব করে তোলে। যেমন:
public void foo(List<? extends Number> numbers) {...}
আপনি একটি পাস করতে পারেন List<Integer>
, List<Float>
, List<Byte>
, ইত্যাদি জন্য numbers
। টাইপ বাউন্ডিং ছাড়া এটি কাজ করবে না - জেনারিকগুলি ঠিক এটি।
অবশেষে, এখানে এমন একটি পদ্ধতির সংজ্ঞা রয়েছে যা ওয়াইল্ডকার্ডকে এমন কিছু করতে ব্যবহার করে যা আমি মনে করি না আপনি অন্য কোনও উপায়ে করতে পারেন:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
numberSuper.add(elem);
}
numberSuper
সংখ্যার একটি তালিকা বা কোনও সংখ্যার সুপার টাইপ (উদাঃ List<Object>
) elem
হতে পারে এবং অবশ্যই সংখ্যা বা কোনও উপ- টাইপ হতে হবে। সমস্ত .add()
আবদ্ধকরণের সাথে, সংকলকটি নিশ্চিত হতে পারে যে এটি টাইপসেফ is
একটি ধরণের ভেরিয়েবল, <T> আপনার নির্ধারিত যেকোন অ-আদিম প্রকার হতে পারে: যে কোনও শ্রেণির প্রকার, কোনও ইন্টারফেস প্রকার, যে কোনও অ্যারের প্রকার বা অন্য কোনও ধরণের ভেরিয়েবল।
সর্বাধিক ব্যবহৃত ধরণের পরামিতিগুলির নাম:
জাভা 7 এ এটির মতো ইনস্ট্যান্ট করার অনুমতি রয়েছে:
Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6
সর্বাধিক ব্যবহৃত ধরণের পরামিতিগুলির নাম:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
আপনি জাভা এসই এপিআই জুড়ে ব্যবহৃত এই নামগুলি দেখতে পাবেন
সংকলক প্রতিটি ওয়াইল্ডকার্ডের জন্য একটি ক্যাপচার তৈরি করবে (উদাহরণস্বরূপ, তালিকায় প্রশ্ন চিহ্ন) যখন এটি কোনও ফাংশন করে:
foo(List<?> list) {
list.put(list.get()) // ERROR: capture and Object are not identical type.
}
তবে ভি এর মতো জেনেরিক টাইপ ঠিক হয়ে যাবে এবং এটিকে একটি জেনেরিক পদ্ধতি তৈরি করা হবে :
<V>void foo(List<V> list) {
list.put(list.get())
}