কার্যকরভাবে চূড়ান্ত বনাম চূড়ান্ত - বিভিন্ন আচরণ


104

এখন পর্যন্ত আমি ভেবেছিলাম যে কার্যকরভাবে চূড়ান্ত এবং চূড়ান্ত কম বেশি সমতুল্য এবং জেএলএস তাদের সাথে একই আচরণ করবে যদি প্রকৃত আচরণে অভিন্ন না হয়। তারপরে আমি এই কনট্রিভাইড দৃশ্যের সন্ধান পেয়েছি:

final int a = 97;
System.out.println(true ? a : 'c'); // outputs a

// versus

int a = 97;
System.out.println(true ? a : 'c'); // outputs 97

স্পষ্টতই, জেএলএস এখানে দুজনের মধ্যে একটি গুরুত্বপূর্ণ পার্থক্য তৈরি করেছে এবং আমি কেন তা নিশ্চিত নই।

আমি অন্যান্য থ্রেড পড়া মত

তবে তারা এ জাতীয় বিশদে যায় না। সর্বোপরি, বিস্তৃত স্তরে তারা বেশ সমতুল্য বলে মনে হয়। কিন্তু গভীর খনন, তারা স্পষ্টতই পৃথক।

এই আচরণটি কী কারণে ঘটছে, কেউ কি জেএলএস সংজ্ঞা প্রদান করতে পারে যা এটি ব্যাখ্যা করে?


সম্পাদনা: আমি অন্য একটি সম্পর্কিত দৃশ্য পেয়েছি:

final String a = "a";
System.out.println(a + "b" == "ab"); // outputs true

// versus

String a = "a";
System.out.println(a + "b" == "ab"); // outputs false

সুতরাং স্ট্রিং ইন্টার্নিং এখানেও অন্যরকম আচরণ করে (আমি এই স্নিপেটটি বাস্তব কোডটিতে ব্যবহার করতে চাই না, কেবল ভিন্ন আচরণ সম্পর্কে কৌতূহলী)।


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

8
নিচে মহান উত্তরে গত উদ্ধৃতি জন্য বাক্যে কথন @Lino একই সব পথ ফিরে এসেছে জাভা 6 : "operands এক ধরনের হয়, তাহলে টি যেখানে টি হল byte, shortবা char, এবং অন্যান্য প্রতীক একটি ধ্রুবক অভিব্যক্তি টাইপ করুন intযার মান টিতে টাইপ করা যায় , তারপরে শর্তসাপেক্ষ অভিব্যক্তির প্রকারটি টি । " --- এমনকি বার্কলে একটি জাভা 1.0 ডক খুঁজে পেয়েছে। একই পাঠ্য । --- হ্যাঁ, এটি সর্বদা সেভাবেই ছিল।
আন্দ্রেয়াস

4
আপনি কীভাবে জিনিসগুলি "সন্ধান" করছেন তা আকর্ষণীয়: P আপনাকে স্বাগতম :)
ফ্যান্ট0 মি

উত্তর:


65

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

একটি পৃষ্ঠ স্তর finalএবং effectively finalস্থানীয় ভেরিয়েবলের জন্য প্রকৃতপক্ষে অভিন্ন। যাইহোক, জেএলএস দুটির মধ্যে একটি স্পষ্ট পার্থক্য তৈরি করে যা এর মতো বিশেষ পরিস্থিতিতে প্রকৃতপক্ষে বিস্তৃত প্রভাব ফেলে।


গেম

ভেরিয়েবল সম্পর্কে JLS§4.12.4 থেকে final:

একটি ধ্রুবক পরিবর্তনশীল একটি হল finalপরিবর্তনশীল আদিম টাইপ বা টাইপ স্ট্রিং যে একটি সঙ্গে সক্রিয়া করা হয় ধ্রুবক অভিব্যক্তি ( §15.29 )। কোনও চলক একটি ধ্রুবক পরিবর্তনশীল কিনা বা শ্রেণীর সূচনা ( §12.4.1 ), বাইনারি সামঞ্জস্যতা ( §13.1 ), পুনঃব্যবহারযোগ্যতা ( §14.22 ), এবং নির্দিষ্ট অ্যাসাইনমেন্ট ( §16.1.1 ) এর সাথে জড়িত থাকতে পারে

যেহেতু intআদিম, ভেরিয়েবল aযেমন ধ্রুবক পরিবর্তনশীল

আরও একই অধ্যায়ে থেকে effectively final:

চূড়ান্ত হিসাবে ঘোষণা করা হয় না এমন কিছু পরিবর্তনশীলগুলি কার্যকরভাবে চূড়ান্ত হিসাবে বিবেচিত হয়: ...

সুতরাং এটি যেভাবে বলা হয় তা থেকে এটি স্পষ্ট যে অন্য উদাহরণে ধ্রুবক পরিবর্তনশীল হিসাবে বিবেচিত aহয় না , কারণ এটি চূড়ান্ত নয় , তবে কেবল কার্যকরভাবে চূড়ান্ত।


আচরণ

এখন আমাদের পার্থক্য রয়েছে, এখন কী চলছে এবং কেন আউটপুট আলাদা তা দেখা যাক।

আপনি ? :এখানে শর্তসাপেক্ষ অপারেটর ব্যবহার করছেন , সুতরাং আমাদের এর সংজ্ঞাটি পরীক্ষা করতে হবে। JLS§15.25 থেকে :

দ্বিতীয় এবং তৃতীয় অপারেন্ড এক্সপ্রেশন অনুসারে শ্রেণিবদ্ধ তিন ধরণের শর্তযুক্ত প্রকাশ রয়েছে: বুলিয়ান শর্তসাপেক্ষ এক্সপ্রেশন , সংখ্যা শর্তযুক্ত এক্সপ্রেশন এবং রেফারেন্স শর্তসাপেক্ষ অভিব্যক্তি

এই ক্ষেত্রে, আমরা JLS§15.25.2 থেকে একটি সংখ্যার শর্তসাপেক্ষ এক্সপ্রেশন সম্পর্কে কথা বলছি :

একটি সংখ্যার শর্তসাপেক্ষ প্রকাশের ধরণটি নিম্নলিখিত হিসাবে নির্ধারিত হয়:

এবং এটি সেই অংশ যেখানে দুটি কেস আলাদাভাবে শ্রেণিবদ্ধ করা হয়।

কার্যকরভাবে চূড়ান্ত

effectively finalএই নিয়মের সাথে মিলে যাওয়া সংস্করণটি :

অন্যথায়, দ্বিতীয় সংখ্যার প্রচার ( §5.6 ) দ্বিতীয় এবং তৃতীয় অপারেন্ডগুলিতে প্রয়োগ করা হয়, এবং শর্তসাপেক্ষ প্রকাশের ধরণটি দ্বিতীয় এবং তৃতীয় অপারেন্ডগুলির প্রচারিত ধরণ।

কোনটি একই আচরণ যেমন আপনি করছেন 5 + 'd'যেমন int + char, যার ফলশ্রুতি intJLS§5.6 দেখুন

সংখ্যাসূচক প্রচারটি সংখ্যার প্রসঙ্গে সমস্ত অভিব্যক্তির প্রচারিত ধরণ নির্ধারণ করে। প্রচারিত প্রকারটি এমনভাবে বেছে নেওয়া হয় যে প্রতিটি অভিব্যক্তি প্রচারিত ধরণের ক্ষেত্রে রূপান্তরিত হতে পারে, এবং গাণিতিক ক্রিয়াকলাপের ক্ষেত্রে অপারেশনটি প্রচারিত ধরণের মানগুলির জন্য সংজ্ঞায়িত করা হয়। একটি সংখ্যার প্রেক্ষাপটে প্রকাশের ক্রম সংখ্যার প্রচারের জন্য তাত্পর্যপূর্ণ নয়। নিয়মগুলি নিম্নরূপ:

[...]

এর পরে, নিম্নলিখিত বিধি অনুসারে কিছু অভিব্যক্তিতে প্রারম্ভিক রূপান্তর ( §5.1.2 ) প্রশস্তকরণ এবং সংকীর্ণ আদিম রূপান্তর ( .15.1.3 ) প্রয়োগ করা হয়:

একটি সংখ্যাগত পছন্দ প্রসঙ্গে, নিম্নলিখিত বিধিগুলি প্রয়োগ হয়:

কোনো অভিব্যক্তি ধরনের হয়, তাহলে intহয় একটি ধ্রুবক অভিব্যক্তি নয় ( §15.29 ), তারপর উন্নীত ধরনের int, এবং অন্যান্য অভিব্যক্তি ধরনের নয় intভোগ করতে আদিম রূপান্তর প্রসার করতে int

তাই সবকিছু উন্নীত করা হয় intযেমন aএকটি হল intইতিমধ্যে। এর আউটপুট ব্যাখ্যা করে 97

চূড়ান্ত

finalভেরিয়েবল সহ সংস্করণটি এই নিয়মের সাথে মিলেছে:

Operands এক ধরনের হয়, তাহলে Tযেখানে Tহয় byte, shortঅথবা char, এবং অন্যান্য প্রতীক একটি হল ধ্রুবক অভিব্যক্তি ( §15.29 ধরনের) intযার মূল্য ধরনের representable হয় T, তারপর শর্তাধীন মত প্রকাশের প্রকার T

চূড়ান্ত পরিবর্তনশীল aটাইপ intএবং ধ্রুবক প্রকাশ (কারণ এটি হয় final) হয়। এটি হিসাবে উপস্থাপনযোগ্য char, ফলস্বরূপ ফলাফল টাইপ char। যে আউটপুট সমাপ্ত a


স্ট্রিং উদাহরণ

স্ট্রিং সমতার সাথে উদাহরণটি একই মূল পার্থক্যের ভিত্তিতে তৈরি হয়, finalভেরিয়েবলগুলি ধ্রুবক প্রকাশ / পরিবর্তনশীল হিসাবে বিবেচিত হয় এবং effectively finalতা হয় না।

জাভাতে, স্ট্রিং ইন্টার্নিং ধ্রুবক মত প্রকাশের উপর ভিত্তি করে, তাই

"a" + "b" + "c" == "abc"

হয় trueপাশাপাশি (আত বাস্তব কোডে এই কনস্ট্রাক্ট ব্যবহার করুন)।

JLS§3.10.5 দেখুন :

তদুপরি, একটি স্ট্রিং আক্ষরিক সর্বদা স্ট্রিং শ্রেণীর একই উদাহরণকে বোঝায়। স্ট্রিং লিটারেলগুলি - বা আরও সাধারণভাবে স্ট্রিংগুলি ধ্রুবক এক্সপ্রেশনগুলির মূল্য ( .215.29 ) - "অভ্যন্তরীণ" হয় যাতে পদ্ধতিটি String.intern( 12.5 ) ব্যবহার করে অনন্য দৃষ্টান্তটি ভাগ করে নেওয়া যায় ।

এটিকে অগ্রাহ্য করা সহজ কারণ এটি প্রাথমিকভাবে আক্ষরিক সম্পর্কে কথা বলছে, তবে এটি আসলে ধ্রুবক প্রকাশের ক্ষেত্রেও প্রযোজ্য।


8
সমস্যাটি হ'ল আপনি পরিবর্তনশীল বা ধ্রুবক হিসাবে ... ? a : 'c'একই আচরণ করার প্রত্যাশা করবেন । প্রকাশের সাথে আপাতদৃষ্টিতে ভুল কিছু নেই। --- এর বিপরীতে, একটি খারাপ ধারণা , কারণ স্ট্রিংগুলির সাথে তুলনা করা দরকার ( জাভাতে আমি কীভাবে স্ট্রিংগুলির তুলনা করব? )। এটি "দুর্ঘটনাক্রমে" যখন ধ্রুবক হয় তখন কাজ করে , এটি স্ট্রিং লিটারালগুলিকে অন্তর্নিহিত করার এক শিখা মাত্র। aa + "b" == "ab"equals()a
আন্দ্রেয়াস

4
@ আন্ড্রেয়াস হ্যাঁ, তবে নোট করুন যে স্ট্রিং ইন্টার্নিং জাভাটির একটি স্পষ্টভাবে সংজ্ঞায়িত বৈশিষ্ট্য। এটি কোনও কাকতালীয় ঘটনা নয় যা কাল বা অন্য কোনও জেভিএমে পরিবর্তিত হতে পারে। কোনও বৈধ জাভা প্রয়োগে "a" + "b" + "c" == "abc"থাকতে হবে true
জাবুজার্ড

10
সত্য, এটি একটি সঠিকভাবে সংজ্ঞায়িত কৌতুক, তবে a + "b" == "ab"এটি এখনও একটি ভুল অভিব্যক্তি । এমনকি আপনি যদি জানেন যে aএকটি হল ধ্রুবক , এটা খুব ত্রুটি-প্রবণ না কল equals()। অথবা ভঙ্গুর একটি ভাল শব্দ, অর্থাত্ ভবিষ্যতে কোডটি বজায় রাখার সময় খুব বিচ্ছিন্ন হওয়ার সম্ভাবনা রয়েছে।
আন্দ্রেস

4
মনে রাখবেন যে কার্যকরভাবে চূড়ান্ত ভেরিয়েবলগুলির প্রাথমিক ডোমেনেও, যেমন ল্যাম্বডা এক্সপ্রেশনগুলিতে তাদের ব্যবহার, পার্থক্যটি রানটাইম আচরণকে বদলে দিতে পারে, অর্থাত্ এটি ক্যাপচারিং এবং একটি নন-ক্যাপচারিং ল্যাম্বডা এক্সপ্রেশনের মধ্যে পার্থক্য তৈরি করতে পারে, এককটি একককে মূল্যায়ন করার পরে , কিন্তু প্রাক্তন একটি নতুন বস্তু উত্পাদন করে। অন্য কথায়, (final) String str = "a"; Stream.of(null, null). <Runnable>map( x -> () -> System.out.println(str)) .reduce((a,b) -> () -> System.out.println(a == b)) .ifPresent(Runnable::run);এর ফলাফলটি পরিবর্তন করুন যখন str(নয়) final
হোলার

7

অন্য দিকটি হ'ল যদি পদ্ধতির শরীরে ভেরিয়েবলটিকে চূড়ান্ত ঘোষণা করা হয় তবে এটি প্যারামিটার হিসাবে পাস হওয়া চূড়ান্ত ভেরিয়েবলের থেকে আলাদা আচরণ করে।

public void testFinalParameters(final String a, final String b) {
  System.out.println(a + b == "ab");
}

...
testFinalParameters("a", "b"); // Prints false

যখন

public void testFinalVariable() {
   final String a = "a";
   final String b = "b";
   System.out.println(a + b == "ab");  // Prints true
}

...
testFinalVariable();

এটি ঘটেছিল কারণ সংকলক জানে যে ভেরিয়েবলটি ব্যবহার final String a = "a"করার ক্ষেত্রে aসর্বদা এর "a"মান থাকবে aএবং "a"সমস্যা ছাড়াই একে অপরের বিনিময় হতে পারে। ভিন্নভাবে, যদি aসংজ্ঞায়িত না হয় finalবা এটি সংজ্ঞায়িত করা হয় finalতবে রানটাইমের সময় এর মান নির্ধারিত হয় (উপরের উদাহরণে যেমন চূড়ান্ত যেখানে aপ্যারামিটার হয়) সংকলক তার ব্যবহারের আগে কিছুই জানে না। সুতরাং রানটাইমের সময় কনকনেটেশন ঘটে এবং ইন্টার্ন পুল ব্যবহার না করে একটি নতুন স্ট্রিং উত্পন্ন হয়।


মূলত আচরণটি হ'ল: সংকলক যদি জানতে পারে যে একটি পরিবর্তনশীল একটি ধ্রুবক হয় তবে ধ্রুবকটি ব্যবহার করার মতোই এটি ব্যবহার করতে পারে।

যদি ভেরিয়েবলটি চূড়ান্ত হিসাবে সংজ্ঞায়িত না হয় (বা এটি চূড়ান্ত হয় তবে রানটাইমের সময় এর মান সংজ্ঞায়িত হয়) কম্পাইলারের কোনও ধ্রুবক হিসাবে এটি পরিচালনা করার কোনও কারণ নেই যদি এর মান ধ্রুবকের সমান হয় এবং এর মান কখনই পরিবর্তন হয় না।


4
এতে অদ্ভুত কিছু নেই :)
ডিবিএল

4
এটি প্রশ্নের আরেকটি দিক।
ডেভিড লরেঞ্জো মেরিনো

4
finelপ্যারামিটারে কীওয়ার্ড প্রয়োগ করা হয়, finalস্থানীয় ভেরিয়েবল ইত্যাদির চেয়ে আলাদা শব্দার্থক প্রয়োগ রয়েছে ...
ডিবিএল

6
প্যারামিটারটি ব্যবহার করা এখানে অপ্রয়োজনীয় অবহেলা। আপনি ঠিক করতে final String a; a = "a";এবং একই আচরণ পেতে পারেন
ইওকাত
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.