জাভা জেনেরিকের কখন <প্রয়োজন হয়? <টি> এর পরিবর্তে টি> প্রসারিত করে এবং সেখানে স্যুইচিংয়ের কোনও নেতিবাচক দিক রয়েছে?


205

নিম্নলিখিত উদাহরণ দেওয়া হয়েছে (হামক্রাস্ট ম্যাচারদের সাথে ইউএনইট ব্যবহার করে):

Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));  

এটি JUnit assertThatপদ্ধতি স্বাক্ষরের সাথে সংকলন করে না :

public static <T> void assertThat(T actual, Matcher<T> matcher)

সংকলক ত্রুটি বার্তাটি হ'ল:

Error:Error:line (102)cannot find symbol method
assertThat(java.util.Map<java.lang.String,java.lang.Class<java.util.Date>>,
org.hamcrest.Matcher<java.util.Map<java.lang.String,java.lang.Class
    <? extends java.io.Serializable>>>)

তবে, যদি আমি assertThatপদ্ধতিটির স্বাক্ষরটি এতে পরিবর্তন করি :

public static <T> void assertThat(T result, Matcher<? extends T> matcher)

তারপরে সংকলনটি কাজ করে।

সুতরাং তিনটি প্রশ্ন:

  1. কেন বর্তমান সংস্করণটি সংকলন করে না? যদিও আমি অস্পষ্টভাবে এখানে covariance বিষয়গুলি বুঝতে পারি, আমি যদি তা করতে পারি তবে অবশ্যই এটি ব্যাখ্যা করতে পারি না।
  2. assertThatপদ্ধতিতে পরিবর্তন করার কোনও খারাপ দিক রয়েছে কি Matcher<? extends T>? আপনি যদি এটি করেন তবে তা ভেঙে যাওয়ার মতো আরও কিছু মামলা রয়েছে?
  3. assertThatJUnit- এ পদ্ধতির জেনারাইজেশনের কোনও বক্তব্য আছে কি ? Matcherশ্রেণী হিসেবে, এটি প্রয়োজন বলে মনে হচ্ছে না যেহেতু JUnit ম্যাচ পদ্ধতি, যা একটি টাইপ নিরাপত্তা যা কিছু না বলপূর্বক একটি প্রয়াস মত কোন জেনেরিক, এবং শুধুমাত্র সৌন্দর্য সঙ্গে টাইপ করা হয় না কল Matcherআসলে শুধু না করবে না ম্যাচ, এবং পরীক্ষা নির্বিশেষে ব্যর্থ হবে। কোনও অনিরাপদ অপারেশন জড়িত নেই (বা তাই এটি মনে হয়)।

রেফারেন্সের জন্য, এখানে JUnit বাস্তবায়ন assertThat:

public static <T> void assertThat(T actual, Matcher<T> matcher) {
    assertThat("", actual, matcher);
}

public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) {
    if (!matcher.matches(actual)) {
        Description description = new StringDescription();
        description.appendText(reason);
        description.appendText("\nExpected: ");
        matcher.describeTo(description);
        description
            .appendText("\n     got: ")
            .appendValue(actual)
            .appendText("\n");

        throw new java.lang.AssertionError(description.toString());
    }
}

এটি লিঙ্কটি খুব দরকারী (জেনেরিক্স, উত্তরাধিকার এবং উপ-প্রকার): ডকস.অরাকল.com
জাফারি

উত্তর:


145

প্রথম - আমাকে আপনাকে http://www.angelikalanger.com/GenericsFAQ/ জাভা জেনারিক্স FAQ.html- এ পরিচালনা করতে হবে - তিনি একটি দুর্দান্ত কাজ করেন।

প্রাথমিক ধারণাটি আপনি ব্যবহার করেন

<T extends SomeClass>

যখন আসল প্যারামিটার SomeClassবা এর কোনও উপ-টাইপ হতে পারে।

আপনার উদাহরণে,

Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));

আপনি বলছেন যে expectedক্লাস অবজেক্টস থাকতে পারে যা প্রয়োগ করে যে কোনও শ্রেণীর প্রতিনিধিত্ব করে Serializable। আপনার ফলাফলের মানচিত্র বলছে এটি কেবল Dateশ্রেণীর অবজেক্টগুলিকে ধারণ করতে পারে।

আপনি যখন ফলাফলে পাস, তুমি সেটিং Tঠিক Mapএর Stringকাছে Dateবর্গ বস্তু, যা মেলে না Mapএর Stringকিছু এর Serializable

একটি জিনিস যাচাই করে দেখুন - আপনি কি নিশ্চিত যে চান Class<Date>এবং না Date? কোনো মানচিত্র Stringথেকে Class<Date>সাধারণভাবে ভয়ঙ্কর দরকারী লাগছে না (সব থাকতে পারে Date.classদৃষ্টান্ত বদলে মান হিসাবে Date)

জেনারাইজাইজিংয়ের জন্য assertThat, ধারণাটি হল যে পদ্ধতিটি নিশ্চিত করতে পারে যে Matcherফলাফলের ধরণটি ফিট করে এমন একটি পাস হয়েছে।


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

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

আপনার পক্ষে Onালাই সঞ্চালিত হয়েছে তা নিশ্চিত করে, ম্যাচার.ম্যাচগুলি () পদ্ধতির কোনও যত্ন নেই, সুতরাং যেহেতু টি কখনই ব্যবহৃত হয় না, কেন এটি জড়িত? (পদ্ধতিটি ফেরতের প্রকারটি বাতিল)
যিশাই

আহ্ - এটাই কি আমি দৃ the়রূপে ডিগ্রি ডিফল্ট না পড়ার জন্য পেয়েছি। দেখে মনে হচ্ছে এটি কেবল কোনও ফিটিং ম্যাচেচার পাস হয়েছে তা নিশ্চিত করার জন্য ...
স্কট স্ট্যানচফিল্ড

28

যারা এই প্রশ্নের উত্তর দিয়েছেন তাদের প্রত্যেককে ধন্যবাদ, এটি সত্যই আমার পক্ষে বিষয়গুলি পরিষ্কার করতে সহায়তা করেছিল। শেষ পর্যন্ত স্কট স্টানফিল্ডের উত্তরটি কীভাবে আমি এটি বুঝতে পেরেছি তার নিকটতমতম সংস্থান পেয়েছিল, তবে যেহেতু তিনি প্রথম এটি লেখার সময় আমি তাকে বুঝতে পারি নি, তাই সমস্যাটি পুনরায় পুনরুদ্ধার করার চেষ্টা করছি যাতে আশা করি অন্য কেউ উপকৃত হবেন।

আমি তালিকার শর্তে প্রশ্নটি পুনঃস্থাপন করতে যাচ্ছি কারণ এটির একটি মাত্র জেনেরিক পরামিতি রয়েছে এবং এটি বুঝতে সহজতর হবে।

প্যারামেট্রাইজড শ্রেণীর উদ্দেশ্য (যেমন উদাহরণের মতো তালিকা <Date>বা মানচিত্র <K, V>) হ'ল এক ঝুঁকিপূর্ণ চাপ প্রয়োগ করা এবং এটি নিরাপদ যে সংকলক গ্যারান্টি রয়েছে (রানটাইম ব্যতিক্রম নয়)।

তালিকার ক্ষেত্রে বিবেচনা করুন। আমার প্রশ্নের সারমর্মটি হ'ল যে পদ্ধতিতে টি এবং টাইপ টাইপ লাগে সেগুলি টিয়ের চেয়ে উত্তরাধিকারের শৃঙ্খলে আরও কিছু কেনের তালিকা গ্রহণ করে না this এই স্বীকৃত উদাহরণটি বিবেচনা করুন:

List<java.util.Date> dateList = new ArrayList<java.util.Date>();
Serializable s = new String();
addGeneric(s, dateList);

....
private <T> void addGeneric(T element, List<T> list) {
    list.add(element);
}

এটি সংকলন করবে না, কারণ তালিকার পরামিতি তারিখগুলির তালিকা, স্ট্রিংগুলির তালিকা নয়। জেনারিকস খুব কম কার্যকর হবে না যদি এটি সংকলন করে।

একই জিনিস মানচিত্রের ক্ষেত্রে প্রযোজ্য <String, Class<? extends Serializable>>এটি মানচিত্রের মতো জিনিস নয় <String, Class<java.util.Date>>। এগুলি সমবায়িক নয়, সুতরাং আমি যদি তারিখের শ্রেণিযুক্ত মানচিত্রটি থেকে মানচিত্রটি নিয়ে সিরিয়ালাইজযোগ্য উপাদানগুলি সহ মানচিত্রে রাখতে চাইতাম তবে এটি সূক্ষ্ম, তবে একটি পদ্ধতির স্বাক্ষর যা বলে:

private <T> void genericAdd(T value, List<T> list)

উভয়ই করতে সক্ষম হতে চায়:

T x = list.get(0);

এবং

list.add(value);

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

দ্বিতীয় প্রশ্নে,

Matcher<? extends T>

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

তৃতীয় প্রশ্নের উত্তর হ'ল চেক করা কার্যকারিতার দিক থেকে কিছুই হারাবে না (এই পদ্ধতিটি সাধারণীকরণ না করা হলে JUnit API এর মধ্যে কোনও সুরক্ষিত টাইপকাস্টিং থাকবে না), তবে তারা অন্য কিছু অর্জন করার চেষ্টা করছেন - স্থিরভাবে নিশ্চিত করুন যে দুটি পরামিতি মিলছে সম্ভবত।

সম্পাদনা (আরও চিন্তা ও অভিজ্ঞতার পরে):

AssertThat পদ্ধতিতে স্বাক্ষরযুক্ত একটি বড় সমস্যা হ'ল একটি ভেরিয়েবল টিকে টি এর জেনেরিক প্যারামিটারের সাথে সমান করার চেষ্টা করা That এটি কার্যকর হয় না, কারণ তারা সমবায়ীয় নয়। সুতরাং উদাহরণস্বরূপ আপনার একটি টি থাকতে পারে যা একটি List<String>তবে তারপরে একটি সংকলনটি সম্পন্ন করে যা সংকলকটি কাজ করে Matcher<ArrayList<T>>। এখন যদি এটি কোনও টাইপ প্যারামিটার না হত তবে জিনিসগুলি ভাল হত, কারণ তালিকা এবং অ্যারেলিস্ট সহকারী হয়, তবে যেহেতু জেনারিক্স, সংকলক হিসাবে অ্যারেলিস্টের প্রয়োজন হয়, এটি যে কারণে স্পষ্ট বলে আশা করি এটি তালিকা সহ্য করতে পারে না উপর থেকে.


যদিও আমি আপস করতে পারি না কেন এখনও পাই না। কেন আমি খেজুরের তালিকাটি সিরিয়ালীয়করণের তালিকায় পরিণত করতে পারি না?
টমাস আহলে

@ থমাস অহলে, কারণ তারপরে স্ট্রিংস বা অন্য কোনও সিরিয়ালাইজেবলগুলি খুঁজে পেলে এটি উল্লেখ করা হয় যে এটি তারিখগুলির একটি তালিকা বলে মনে করে ingালাই ত্রুটিগুলিতে চলে যাবে।
যিশাই 11 ই

আমি দেখতে পাচ্ছি, তবে কি যদি আমি কোনওভাবে পুরানো রেফারেন্সটি থেকে মুক্তি পেয়ে যাই, যেন আমি টাইপযুক্ত List<Date>কোনও পদ্ধতি থেকে ফিরে এসেছি List<Object>? এটি জাভা দ্বারা অনুমোদিত না হলেও সঠিকভাবে নিরাপদ হওয়া উচিত।
থমাস আহলে 11

14

এটি উত্পন্ন হয়:

Class<? extends Serializable> c1 = null;
Class<java.util.Date> d1 = null;
c1 = d1; // compiles
d1 = c1; // wont compile - would require cast to Date

আপনি দেখতে পাচ্ছেন শ্রেণি রেফারেন্স সি 1 এ একটি দীর্ঘ উদাহরণ থাকতে পারে (যেহেতু একটি নির্দিষ্ট সময়ে অন্তর্নিহিত অবজেক্টটি থাকতে পারে List<Long>) তবে অবশ্যই "অজানা" শ্রেণীর তারিখের কোনও গ্যারান্টি নেই বলে কোনও তারিখে কাস্ট করা যাবে না। এটি টাইপসেফ নয়, তাই সংকলক এটি এটিকে অস্বীকার করে।

তবে, আমরা যদি অন্য কোনও বস্তুর পরিচয় করিয়ে দিই, তালিকা বলি (আপনার উদাহরণে এই বস্তুটি ম্যাচার), তবে নিম্নলিখিতটি সত্য হয়ে যায়:

List<Class<? extends Serializable>> l1 = null;
List<Class<java.util.Date>> l2 = null;
l1 = l2; // wont compile
l2 = l1; // wont compile

... তবে তালিকার ধরণটি যদি হয়ে যায়? টি এর পরিবর্তে টি প্রসারিত করে ....

List<? extends Class<? extends Serializable>> l1 = null;
List<? extends Class<java.util.Date>> l2 = null;
l1 = l2; // compiles
l2 = l1; // won't compile

আমি মনে করি পরিবর্তন করে Matcher<T> to Matcher<? extends T>আপনি মূলত l1 = l2 নির্ধারণের অনুরূপ দৃশ্যের পরিচয় দিচ্ছেন;

এটি এখনও খুব বিভ্রান্তিকরভাবে নেস্টেড ওয়াইল্ডকার্ড পেয়েছে তবে আশা করা যায় যে এটি কীভাবে আপনি একে অপরের জেনেরিক রেফারেন্স নির্ধারণ করতে পারেন তা জেনারিকগুলি বুঝতে কেন সহায়তা করে sense এটি আরও বিভ্রান্তিকর হয় যেহেতু আপনি যখন ফাংশন কল করার সময় সংকলকটি টি এর ধরণের অনুমান করে যাচ্ছেন (আপনি স্পষ্টভাবে বলছেন না যে এটি টি ছিল)।


9

আপনার মূল কোডটি সংকলন না করার কারণটি এর অর্থ <? extends Serializable>এই নয় যে "কোনও শ্রেণি যা সিরিয়ালাইজেবল প্রসারিত করে", তবে "কিছু অজানা তবে নির্দিষ্ট শ্রেণি যা সিরিয়ালাইজযোগ্যকে প্রসারিত করে।"

উদাহরণস্বরূপ, লিখিত হিসাবে কোড দেওয়া, এটি নির্ধারণ new TreeMap<String, Long.class>()>করা পুরোপুরি বৈধ expected। যদি সংকলক কোডটি সংকলনের অনুমতি দেয় assertThat()তবে সম্ভবত এটি ভেঙে যায় কারণ এটি মানচিত্রে পাওয়া Dateবস্তুর পরিবর্তে বস্তুর প্রত্যাশা করবে Long


1
আমি বেশীরভাগ অনুসরণ করছি না - যখন আপনি "মানে ... না ... তবে ...": পার্থক্য কী? (যেমন, "
সংখ্যাত

হ্যাঁ, এটি কিছুটা বিশ্রী; কীভাবে এটি আরও ভালভাবে প্রকাশ করবেন তা নিশ্চিত নন ... ""? কোনও প্রকার যা অজানা, কোন প্রকারের সাথে মেলে না? "
এরিকসন

1
এটি ব্যাখ্যা করতে কী সাহায্য করতে পারে তা হ'ল "যে কোনও শ্রেণি যা সিরিয়ালাইজটেবল প্রসারিত করে" এর জন্য আপনি কেবল ব্যবহার করতে পারেন<Serializable>
c0der

8

ওয়াইল্ডকার্ডগুলি বোঝার জন্য আমার একটি উপায় হ'ল ভাবা যে ওয়াইল্ডকার্ড জেনেরিক রেফারেন্স দিয়ে "সম্ভবত" থাকতে পারে এমন সম্ভাব্য বস্তুর প্রকারটি নির্দিষ্ট করে না, তবে অন্যান্য জেনেরিক রেফারেন্সগুলির সাথে এটি সামঞ্জস্যপূর্ণ (এটি বিভ্রান্তিকর মনে হতে পারে ...) এর মতো, প্রথম উত্তরটি এটির কথার ক্ষেত্রে খুব বিভ্রান্তিকর।

অন্য কথায়, এর List<? extends Serializable>অর্থ আপনি সেই তালিকাটি অন্যান্য তালিকাগুলিতে নির্ধারণ করতে পারেন যেখানে টাইপটি কিছু অজানা প্রকার যা সিরিয়ালাইজেবলের একটি সাবক্লাস। এটিকে একটি একক তালিকা সিরিয়ালাইজেবলের সাবক্লাস ধারণ করতে সক্ষম হওয়ার বিষয়টি বিবেচনা করবেন না (কারণ এটি ভুল শব্দার্থবিজ্ঞান এবং জেনেরিকের একটি ভুল বোঝাবুঝির দিকে নিয়ে যায়)।


এটি অবশ্যই সহায়তা করে তবে "বিভ্রান্তকর শোনায়" এটিকে "বিভ্রান্তিকর শব্দ" দিয়ে প্রতিস্থাপন করা হয়। অনুসরণ হিসাবে, সুতরাং, কেন এই ব্যাখ্যা অনুসারে, ম্যাচারের সাথে পদ্ধতিটি <? extends T>সংকলন করে?
যিশাই

যদি আমরা তালিকাটি << শিরোনামযোগ্য> হিসাবে সংজ্ঞায়িত করি তবে এটি একই কাজটি ঠিক করে? আমি তোমার ২ য় প্যারা নিয়ে কথা বলছি অর্থাত্ পলিমারফিজম এটি পরিচালনা করবে?
সুপুন উইজিরথনে

3

আমি জানি এটি একটি পুরানো প্রশ্ন তবে আমি এমন একটি উদাহরণ ভাগ করতে চাই যা আমি মনে করি সীমিত ওয়াইল্ডকার্ডগুলি বেশ ভালভাবে ব্যাখ্যা করে। java.util.Collectionsএই পদ্ধতি প্রস্তাব:

public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

আমাদের যদি একটি তালিকা থাকে Tতবে তালিকায় অবশ্যই প্রকারের প্রসারের উদাহরণ থাকতে পারে T। যদি তালিকায় প্রাণী থাকে, তবে তালিকায় কুকুর এবং বিড়াল (উভয় প্রাণী) থাকতে পারে। কুকুরের একটি সম্পত্তি "ওয়ুফভলিউম" এবং বিড়ালের একটি সম্পত্তি আছে "ম্যালোভলিউম।" যদিও আমরা এই বৈশিষ্ট্যগুলিকে বিশেষত সাবক্লাসের উপর ভিত্তি করে বাছাই করতে পছন্দ Tকরতে পারি, তবে কীভাবে আমরা এই পদ্ধতিটি এটি করার আশা করতে পারি? তুলনাকারীর একটি সীমাবদ্ধতা হ'ল এটি কেবলমাত্র এক ধরণের ( T) দুটি মাত্র জিনিসের তুলনা করতে পারে । সুতরাং, সহজভাবে একটি প্রয়োজন Comparator<T>এই পদ্ধতিটি ব্যবহারযোগ্য করে তুলবে। তবে, এই পদ্ধতির স্রষ্টা স্বীকৃতি দিয়েছিলেন যে যদি কোনও কিছু হয় Tতবে তা এটি সুপার ক্লাসের উদাহরণও T। অতএব, তিনি আমাদের Tতুলককারী বা যেকোন সুপারক্লাস T, অর্থাৎ ব্যবহার করতে পারবেন ? super T


1

আপনি যদি ব্যবহার করেন

Map<String, ? extends Class<? extends Serializable>> expected = null;

হ্যাঁ, এটি আমার উপরের উত্তরটি কীভাবে চালাচ্ছিল তা এই জাতীয়।
গ্রিনিমিনি

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