এটি একটি সত্যিই আকর্ষণীয় প্রশ্ন। উত্তর, আমি ভীত, জটিল।
TL; ড
পার্থক্যটি কার্যকর করা জাওয়ার ধরণের ইনফারেন্স স্পেসিফিকেশনের কিছু গভীরভাবে পড়ার সাথে জড়িত , তবে মূলত এটি এটিকে ফুটিয়ে তোলে:
- অন্যান্য সমস্ত জিনিস সমান, সংকলক এটি করতে পারে সবচেয়ে সুনির্দিষ্ট ধরণের অনুমান করে ।
- যাইহোক, যদি এটি কোনও প্রয়োজনীয় প্যারামিটারের জন্য কোনও বিকল্প খুঁজে পেতে পারে যা সমস্ত প্রয়োজনীয়তা পূরণ করে, তবে সংকলনটি সফল হবে, তবে প্রতিস্থাপনটি অস্পষ্ট ।
- কারণ
with
একটি (স্বীকার্যভাবে অস্পষ্ট) প্রতিস্থাপন রয়েছে যা এখানে সমস্ত প্রয়োজনীয়তা পূরণ করে R
:Serializable
- কারণ
withX
, অতিরিক্ত ধরণের পরামিতি প্রবর্তন সীমাবদ্ধতার বিষয়টি বিবেচনা না করে প্রথমে F
সংকলকটিকে সমাধান R
করতে বাধ্য করে F extends Function<T,R>
। R
(আরও সুনির্দিষ্ট) এর সমাধান করে String
যার অর্থ হল F
ব্যর্থতার অনুক্রম।
এই শেষ বুলেট পয়েন্টটি সর্বাধিক গুরুত্বপূর্ণ, তবে সর্বাধিক হ্যান্ড-ওয়েভ। আমি এটির উচ্চারণের আরও ভাল সংক্ষিপ্ত উপায়টি ভাবতে পারি না, সুতরাং আপনি যদি আরও বিশদ চান, তবে আমি আপনাকে নীচের সম্পূর্ণ ব্যাখ্যাটি পড়ার পরামর্শ দিচ্ছি।
এই উদ্দেশ্যমূলক আচরণ?
আমি এখানে অবয়ব বেরিয়ে যান, এবং বলতে যাচ্ছি কোন ।
আমি প্রস্তাবটিতে একটি ত্রুটি থাকার পরামর্শ দিচ্ছি না, আরও বেশি ( withX
ভাষার ক্ষেত্রে ) ডিজাইনাররা তাদের হাত তুলে বলেছে যে "কিছু পরিস্থিতি রয়েছে যেখানে টাইপ অনুকরণ খুব শক্ত হয়ে যায়, তাই আমরা কেবল ব্যর্থ হব" । যদিও সংকলকের শ্রদ্ধার সাথে আচরণটি withX
আপনি যা চান তা মনে হচ্ছে, তবে আমি এটিকে ইতিবাচকভাবে নকশাকৃত সিদ্ধান্তের পরিবর্তে বর্তমান অনুমানের একটি ঘটনামূলক পার্শ্ব-প্রতিক্রিয়া বলে বিবেচনা করব।
এটি গুরুত্বপূর্ণ, কারণ এটি প্রশ্নটি অবহিত করে আমার অ্যাপ্লিকেশন ডিজাইনে কি এই আচরণের উপর নির্ভর করা উচিত? আমি যুক্তি দিয়ে বলব যে আপনার উচিত নয়, কারণ আপনি গ্যারান্টি দিতে পারবেন না যে ভাষার ভবিষ্যতের সংস্করণগুলি এভাবে চলতে থাকবে।
যদিও এটি সত্য যে ভাষা ডিজাইনাররা তাদের অ্যাপ্লিকেশন / নকশা / সংকলক আপডেট করার সময় বিদ্যমান অ্যাপ্লিকেশনগুলি না ভাঙ্গার জন্য খুব চেষ্টা করে, সমস্যাটি হ'ল আপনি যে আচরণের উপর নির্ভর করতে চান সেটি বর্তমানে কম্পাইলারটি ব্যর্থ হয় (যেমন বিদ্যমান অ্যাপ্লিকেশন নয় )। ল্যাঙ্গেজ আপডেটগুলি সর্বদা নন-সংকলন কোডকে সংকলন কোডে পরিণত করে। উদাহরণস্বরূপ, নিম্নলিখিত কোডটি জাভা 7 তে সংকলন না করার গ্যারান্টিযুক্ত হতে পারে তবে জাভা 8 তে সংকলন করবে :
static Runnable x = () -> System.out.println();
আপনার ব্যবহারের ক্ষেত্রে কেস আলাদা নয়।
আমি আপনার withX
পদ্ধতিটি ব্যবহারের বিষয়ে সতর্ক হতে পারার আরেকটি কারণ হ'ল F
প্যারামিটার। সাধারণত, কোনও পদ্ধতিতে জেনেরিক ধরণের প্যারামিটার (যা প্রত্যাবর্তনের ধরণে প্রদর্শিত হয় না) স্বাক্ষরের একাধিক অংশের প্রকারগুলি একসাথে আবদ্ধ করতে বিদ্যমান। এটা বলছে:
আমি কী তা যত্নশীল না T
, তবে নিশ্চিত হতে চাই যে যেখানেই আমি T
এটি একই ধরণের ব্যবহার করি।
যৌক্তিকভাবে, তারপরে, আমরা প্রত্যাশা করব যে প্রতিটি ধরণের প্যারামিটার কোনও পদ্ধতিতে স্বাক্ষরে কমপক্ষে দুবার প্রদর্শিত হবে, অন্যথায় "এটি কিছু করছে না"। F
আপনার withX
কেবলমাত্র একবার স্বাক্ষরে উপস্থিত হয়, যা আমাকে এই টাইপের প্যারামিটার ব্যবহারটি ভাষার এই বৈশিষ্ট্যের অভিপ্রায় অনুসারে ইনলাইন না করার পরামর্শ দেয় ।
একটি বিকল্প বাস্তবায়ন
এটিকে কিছুটা আরও "অভিযুক্ত আচরণ" উপায়ে কার্যকর করার একটি উপায় হ'ল আপনার with
পদ্ধতিটিকে 2 এর শৃঙ্খলে বিভক্ত করা:
public class Builder<T> {
public final class With<R> {
private final Function<T,R> method;
private With(Function<T,R> method) {
this.method = method;
}
public Builder<T> of(R value) {
// TODO: Body of your old 'with' method goes here
return Builder.this;
}
}
public <R> With<R> with(Function<T,R> method) {
return new With<>(method);
}
}
এটি নিম্নলিখিত হিসাবে ব্যবহার করা যেতে পারে:
b.with(MyInterface::getLong).of(1L); // Compiles
b.with(MyInterface::getLong).of("Not a long"); // Compiler error
এটিতে আপনার মতো বহিরাগত ধরণের প্যারামিটার অন্তর্ভুক্ত নয় withX
। পদ্ধতিটিকে দুটি স্বাক্ষরে বিভক্ত করার মাধ্যমে, টাইপ-সুরক্ষা দৃষ্টিকোণ থেকে আপনি যা করার চেষ্টা করছেন তার অভিপ্রায়টি আরও ভালভাবে প্রকাশ করে:
- প্রথম পদ্ধতিটি একটি শ্রেণি সেট করে (
With
) যা পদ্ধতি রেফারেন্সের ভিত্তিতে প্রকারটি সংজ্ঞায়িত করে।
- স্ক্যান্ড পদ্ধতি (
of
) আপনি পূর্বে যা সেট আপ করেছিলেন তার সাথে সামঞ্জস্য হওয়ার ধরণের সীমাবদ্ধ করে value
।
ভাষার ভবিষ্যতের কোনও সংস্করণ এটি সঙ্কলন করতে সক্ষম হওয়ার একমাত্র উপায় হ'ল যদি বাস্তবায়িত পূর্ণ হাঁস-টাইপিংয়ের বিষয়টি অসম্ভব বলে মনে হয়।
এই পুরো জিনিসটিকে অপ্রাসঙ্গিক করে তোলার জন্য একটি চূড়ান্ত নোট: আমি মনে করি মকিতো (এবং বিশেষত এর স্টিবিং কার্যকারিতা) আপনার "টাইপ নিরাপদ জেনেরিক বিল্ডার" দিয়ে আপনি যা অর্জন করার চেষ্টা করছেন তা মূলত ইতিমধ্যে তা করতে পারে। এর পরিবর্তে আপনি কেবল এটি ব্যবহার করতে পারেন?
সম্পূর্ণ (ish) ব্যাখ্যা
আমি উভয় এবং জন্য টাইপ অনুমান পদ্ধতি মাধ্যমে কাজ করতে যাচ্ছি । এটি বেশ দীর্ঘ, তাই এটি ধীরে ধীরে নিন। দীর্ঘ হওয়া সত্ত্বেও আমি এখনও প্রচুর বিবরণ রেখেছি। নিজেকে সঠিকভাবে বোঝানোর জন্য আপনি বিশদটি আরও বিশদে (লিঙ্কগুলি অনুসরণ করুন) উল্লেখ করতে চাইতে পারেন (আমি ভালই ভুল করেছি)।with
withX
এছাড়াও, জিনিসগুলি আরও সহজ করার জন্য, আমি আরও একটি ন্যূনতম কোড নমুনা ব্যবহার করতে যাচ্ছি। মূল পার্থক্য হল, এটি বিনিময়সমূহ হয় Function
জন্য Supplier
, তাই কম ধরনের এবং খেলার মধ্যে পরামিতি। এখানে একটি সম্পূর্ণ স্নিপেট যা আপনার বর্ণিত আচরণটির পুনরুত্পাদন করে:
public class TypeInference {
static long getLong() { return 1L; }
static <R> void with(Supplier<R> supplier, R value) {}
static <R, F extends Supplier<R>> void withX(F supplier, R value) {}
public static void main(String[] args) {
with(TypeInference::getLong, "Not a long"); // Compiles
withX(TypeInference::getLong, "Also not a long"); // Does not compile
}
}
আসুন প্রযোজ্য অনুক্রমের টাইপটির মাধ্যমে কাজ করুন এবং প্রতিটি পদ্ধতি পরিবর্তনের জন্য অনুরোধ পদ্ধতিটি টাইপ করুন :
with
আমাদের আছে:
with(TypeInference::getLong, "Not a long");
প্রাথমিক বাউন্ড সেট, বি 0 , হ'ল:
সমস্ত পরামিতি এক্সপ্রেশন প্রয়োগের জন্য প্রাসঙ্গিক ।
অতএব, প্রয়োগের অনুক্রমের জন্য প্রাথমিক সীমাবদ্ধতা নির্ধারণ , সি , হ'ল:
TypeInference::getLong
সঙ্গে সামঞ্জস্যপূর্ণ Supplier<R>
"Not a long"
সঙ্গে সামঞ্জস্যপূর্ণ R
এই হ্রাস আবদ্ধ সেটে বি 2 এর:
R <: Object
( বি 0 থেকে )
Long <: R
(প্রথম সীমাবদ্ধতা থেকে)
String <: R
(দ্বিতীয় বাধা থেকে)
যেহেতু এই বাউন্ড 'ধারণ করে না মিথ্যা ', এবং (আমি অনুমান) রেজল্যুশন এর R
সফল (দান Serializable
), তারপর আবাহন প্রযোজ্য।
সুতরাং, আমরা অনুরোধের প্রকারের অনুক্রমের দিকে এগিয়ে যাই ।
নতুন সীমাবদ্ধ সেট, সি , সম্পর্কিত ইনপুট এবং আউটপুট ভেরিয়েবলগুলি সহ:
TypeInference::getLong
সঙ্গে সামঞ্জস্যপূর্ণ Supplier<R>
- ইনপুট ভেরিয়েবল: কিছুই নয়
- আউটপুট ভেরিয়েবল:
R
এটিতে ইনপুট এবং আউটপুট ভেরিয়েবলগুলির মধ্যে কোনও আন্তঃনির্ভরতা নেই , তাই একক ধাপে হ্রাস করা যায়, এবং চূড়ান্ত বাউন্ড সেট, বি 4 , বি 2 এর সমান । অতএব, রেজোলিউশন আগের মতো সফল হয় এবং সংকলক স্বস্তির দীর্ঘশ্বাস ফেলে!
withX
আমাদের আছে:
withX(TypeInference::getLong, "Also not a long");
প্রাথমিক বাউন্ড সেট, বি 0 , হ'ল:
R <: Object
F <: Supplier<R>
প্রয়োগের ক্ষেত্রে কেবলমাত্র দ্বিতীয় প্যারামিটারের অভিব্যক্তিই প্রাসঙ্গিক । প্রথমটি ( TypeInference::getLong
) নয়, কারণ এটি নিম্নলিখিত শর্তটি পূরণ করে:
যদি m
একটি জেনেরিক পদ্ধতি হয় এবং পদ্ধতিটির অনুরোধটি সুস্পষ্ট ধরণের আর্গুমেন্ট সরবরাহ করে না, একটি সুস্পষ্টভাবে টাইপ করা ল্যাম্বদা এক্সপ্রেশন বা একটি সঠিক পদ্ধতি রেফারেন্স এক্সপ্রেশন যার জন্য সংশ্লিষ্ট টার্গেট টাইপ (এর স্বাক্ষর থেকে প্রাপ্ত হিসাবে m
) একটি টাইপ প্যারামিটার m
।
অতএব, প্রয়োগের অনুক্রমের জন্য প্রাথমিক সীমাবদ্ধতা নির্ধারণ , সি , হ'ল:
"Also not a long"
সঙ্গে সামঞ্জস্যপূর্ণ R
এই হ্রাস আবদ্ধ সেটে বি 2 এর:
R <: Object
( বি 0 থেকে )
F <: Supplier<R>
( বি 0 থেকে )
String <: R
(সীমাবদ্ধতা থেকে)
আবার, যেহেতু এই আবদ্ধ 'ধারণ করে না মিথ্যা ', এবং রেজল্যুশন এর R
সফল (দান String
), তারপর আবাহন প্রযোজ্য।
অনুরোধের প্রকারের অনুমান আরও একবার ...
এবার, সম্পর্কিত ইনপুট এবং আউটপুট ভেরিয়েবলগুলির সাথে নতুন সীমাবদ্ধ সেট, সি :
TypeInference::getLong
সঙ্গে সামঞ্জস্যপূর্ণ F
- ইনপুট ভেরিয়েবল:
F
- আউটপুট ভেরিয়েবল: কিছুই নয়
আবার ইনপুট এবং আউটপুট ভেরিয়েবলের মধ্যে আমাদের কোনও আন্তঃনির্ভরতা নেই । তবে এই সময়, সেখানে হয় একটি ইনপুট পরিবর্তনশীল ( F
,) তাই আমরা উচিত নয় সমাধান চেষ্টা করার আগে এই হ্রাস । সুতরাং, আমরা আমাদের বাউন্ড সেট বি 2 দিয়ে শুরু করব ।
আমরা V
নিম্নোক্তভাবে একটি উপসেট নির্ধারণ করি :
সমাধানের জন্য ইনফেরেন্স ভেরিয়েবলের একটি সেট দেওয়া হয়েছে V
, এই সেটটির ইউনিট এবং সমস্ত ভেরিয়েবলগুলির যোজন হওয়া উচিত যার উপর এই সেটে অন্তত একটি ভেরিয়েবলের রেজোলিউশন নির্ভর করে।
বি 2F
তে দ্বিতীয় গণ্ডির মধ্যে , এর রেজোলিউশন নির্ভর করে R
, তাই V := {F, R}
।
আমরা V
নিয়ম অনুসারে একটি উপসেট বেছে নিই :
দিন { α1, ..., αn }
একটি খালি মধ্যে uninstantiated ভেরিয়েবল উপসেট হতে V
সবার জন্য যেমন যে ঝ) i (1 ≤ i ≤ n)
, যদি αi
একটি পরিবর্তনশীল রেজল্যুশন উপর নির্ভর করে β
, তারপর পারেন β
একটি ইনস্ট্যান্স আছে বা নেই কিছু j
যেমন যে β = αj
; এবং ii) { α1, ..., αn }
এই সম্পত্তি সহ খালি খালি কোনও উপযুক্ত উপসেট নেই ।
V
এই সম্পত্তি সন্তুষ্ট যে একমাত্র উপসেট হয় {R}
।
তৃতীয় বাউন্ড ( String <: R
) ব্যবহার করে আমরা তাত্ক্ষণিকভাবে R = String
এটিকে আমাদের আবদ্ধ সেটটিতে অন্তর্ভুক্ত করি। R
এখন সমাধান করা হয়, এবং দ্বিতীয় সীমা কার্যকরভাবে হয়ে যায় F <: Supplier<String>
।
(সংশোধিত) দ্বিতীয় সীমাটি ব্যবহার করে, আমরা তাত্ক্ষণিক করি F = Supplier<String>
। F
এখন সমাধান করা হয়।
এখন এটি F
সমাধান হয়েছে, আমরা নতুন বাধা ব্যবহার করে হ্রাস নিয়ে এগিয়ে যেতে পারি :
TypeInference::getLong
সঙ্গে সামঞ্জস্যপূর্ণ Supplier<String>
- ... সাথে সামঞ্জস্যপূর্ণ হ্রাস
Long
String
- ... যা কমে যায় মিথ্যে
... এবং আমরা একটি সংকলক ত্রুটি পেয়েছি!
'বর্ধিত উদাহরণ' সম্পর্কিত অতিরিক্ত নোট
এক্সটেন্ডেড উদাহরণ কয়েক আকর্ষণীয় ক্ষেত্রে সরাসরি উপরে ক্রিয়াকাণ্ড দ্বারা আচ্ছাদিত করা হয় না প্রশ্নগুলি সৌন্দর্য মধ্যে:
- যেখানে মানের ধরণটি পদ্ধতিটি ফেরতের প্রকারের উপ -প্রকার (
Integer <: Number
)
- যেখানে কার্যকরী ইন্টারফেসটি অনুমানযুক্ত ধরণের (যেমন
Consumer
পরিবর্তে Supplier
) তুলনায় বিপরীত হয়
বিশেষত, প্রদত্ত আমন্ত্রণগুলির মধ্যে 3 টি ব্যাখ্যাতে বর্ণিত বর্ণনার সাথে 'পৃথক' সংকলক আচরণের সম্ভাব্য হিসাবে পরামর্শ দিচ্ছে:
t.lettBe(t::setNumber, "NaN"); // Does not compile :-)
t.letBeX(t::getNumber, 2); // !!! Does not compile :-(
t.lettBeX(t::setNumber, 2); // Compiles :-)
এই 3 এর দ্বিতীয়টি withX
উপরের মত ঠিক একই সূচনা প্রক্রিয়াটির মধ্য দিয়ে যাবে (কেবল Long
সাথে Number
এবং এর String
সাথে প্রতিস্থাপন করুন Integer
)। এই এখনো অন্য কারণ কেন আপনি আপনার বর্গ নকশা জন্য এই ব্যর্থ টাইপ অনুমান আচরণের উপর নির্ভর করা উচিত নয় প্রকাশ, যেমন এখানে কম্পাইল করার ব্যর্থতা সম্ভাবনা থাকে না একটি আকাঙ্খিত আচরণ।
অন্য 2 (এবং প্রকৃতপক্ষে অন্য যে কোনও আবেদনের Consumer
মধ্য দিয়ে আপনি কাজ করতে চান) এর জন্য, যদি আপনি উপরের কোনও পদ্ধতির জন্য নির্ধারিত টাইপ অনুক্রমের পদ্ধতিটি (যেমন with
প্রথমটির withX
জন্য, তৃতীয়)। আপনার কেবলমাত্র একটি ছোট পরিবর্তন অবশ্যই খেয়াল রাখতে হবে:
- প্রথম প্যারামিটার (চালু বাধ্যতা
t::setNumber
সঙ্গে সামঞ্জস্যপূর্ণ Consumer<R>
) হবে কমাতে করার R <: Number
পরিবর্তে Number <: R
এটির জন্য মতো Supplier<R>
। হ্রাস সম্পর্কিত লিঙ্কযুক্ত ডকুমেন্টেশনে এটি বর্ণিত হয়েছে।
আমি এটিকে অতিরিক্ত জ্ঞানের এই টুকরো দিয়ে সজ্জিত উপরের পদ্ধতিগুলির মধ্যে কারের সাথে খোদাই করে কাজ করার একটি সংক্ষিপ্তসার হিসাবে রেখেছি, কোনও নির্দিষ্ট প্রার্থনা কেন সংকলন করে না বা ঠিক তা কেন সংকলন করে না তা নিজেই তা দেখানোর জন্য।