سؤال

أواجه مشكلة صغيرة في فهم تباين الأساليب عند التحميل الزائد.

في حين أن هذا يعمل بشكل مثالي بسبب التباين في نوع العودة

class Bla 
class Fasel extends Bla 

trait Test[A] {
 def tester(): Bla = new Bla
}

class FooTest[A](a: A) extends Test[A] {
 override def tester(): Fasel = new Fasel                                      
}

يفشل هذا واحد على الرغم من أن الوظائف تتعارض مع أنواع المعلمات الخاصة بها.

class Bla 
class Fasel extends Bla 

trait Test[A] {
 def tester(a: Fasel): Bla = new Bla                                           
}

class FooTest[A](a: A) extends Test[A] {
 override def tester(a: Bla): Fasel = new Fasel
}

ما الذي أخطأ هنا؟ أي مؤشرات؟

التحيات ، رايشو

هل كانت مفيدة؟

المحلول

هناك شيئان يحدثان هنا:

  1. وظيفة وطريقة ليست نفس الشيء
  2. الأساليب ليست متعددة الأشكال في أنواع المعلمات الخاصة بهم

لك tester الطريقة هي طريقة ، ليس أ Function1. يمكن رفعه إلى وظيفة باستخدام بناء جملة السفلية:

val f = (new FooTest[String]).tester _ // Fasel => Bla

ستكون هذه الوظيفة متغيرًا في نوع الإدخال. (يجدر القول ، مع ذلك ، يعمل لا يمكن أن تكون معلمة وأيضًا يستحق القول أنه كان عليّ أن أحصل على مثال Foo أو FooTest من أجل الحصول على كائن وظيفة ل tester طريقة. هذا بالطبع يتبع من الملاحظة الأولى!)

الوظيفة هي كائن ، لا يمكن تجاوزه لأن هذا لا معنى له. يمكن تجاوز الأساليب. ومع ذلك ، كما قلت أعلاه ، فإن التجاوز ليس متعدد الأشكال في أنواع معلمات الطريقة. على سبيل المثال:

class A {
  def foo(a : Any) = println("A: " + a)
}

class B extends A {
  override def foo(s : String) = println("B " + s) //will not compile!
}

الطريقتان في مثالي أعلاه هما طريقتان منفصلتان: يعمل الإرسال الديناميكي فقط على هدف الطريقة (أي الكائن الذي يتم استدعاؤه عليه).

في ما سبق ، على سبيل المثال ، إذا قمت بإزالة override الإعلان ، سيتم تجميع الكود. إذا قمت بتشغيل ما يلي:

(new B).foo(1)   //prints A 1
(new B).foo("s") //prints B s

هذا لأنه على الرغم من أن كلتا الطريقتين تسمى foo, ، إنها طرق مختلفة تمامًا (أي لدي محملة foo, ، ليس تجاوز هو - هي). من الأفضل أن يُفهم أن وسيطات الطريقة (بما في ذلك أنواعها) تشكل جزءًا من هذه الطريقة الفريدة اسم. طريقة واحدة تتجاوز طريقة أخرى فقط إذا كان لديهم نفس الشيء بالضبط اسم.


في الأساس ، قمت بربط ما هما اثنان منفصل وغير مرتبط الأشياء في سؤالك ، والتي سأضعها من أجل الوضوح:

  • تعليقات التباين على Function1 حدد ما يعنيه أن تكون وظيفة واحدة نوعًا فرعيًا لآخر (وبالتالي قابلة للتعيين إلى مرجع من نوع معين).
  • يمكن تجاوز الأساليب على الفئات الفرعية وتحدد مواصفات اللغة قواعد عندما تحدث مثل هذه الغالبة.

نصائح أخرى

المقتطفات ذات الصلة للمواصفات:

أنواع الأسلوب

يشار إلى نوع الطريقة داخليًا (Ps)U ، أين (Ps) هو تسلسل أسماء وأنواع المعلمات (p1 :T1,...,pn :Tn) للبعض n≥0 و U هو نوع (القيمة أو الطريقة). يمثل هذا النوع طرقًا مسماة تأخذ الحجج المسمى p1, ..., pn من الأنواع T1,...,Tn وهذا يعود نتيجة النوع U.

أنواع الأسلوب غير موجودة كنوع من القيم. إذا تم استخدام اسم الطريقة كقيمة ، فسيتم تحويل نوعه ضمنيًا إلى نوع وظيفة مقابلة (الفقرة 6.26).

تجاوز

عضوا M من صنف C الذي - التي اعواد الكبريت (§5.1.3) عضو غير خاص M′ من فئة قاعدة من C يقال لتجاوز هذا العضو. في هذه الحالة ، ربط العضو الغالب M يجب ترحيل (§3.5.2) ربط العضو المتجول M′.

المطابقة

إذا Ti ≡ Ti′ إلى عن على i = 1, ..., n و U يتوافق مع U′ ثم نوع الطريقة (p1 : T1,...,pn :Tn)U يتوافق مع (p1′ :T1′,...,pn′ :Tn′)U′.

Songumes

إعلان أو تعريف في نوع مركب من نوع الفئة C يستلزم إعلانًا آخر يحمل نفس الاسم في نوع مركب أو نوع فئة C′ ، إذا كان أحد ما يلي.

  • إعلان القيمة أو التعريف الذي يحدد اسم X مع النوع T يضع قيمة أو إعلان طريقة يحدد x مع النوع T′, ، قدمت T <: T′.

يمكنك تجاوز وتغيير نوع الإرجاع إلى نوع فرعي ، ولكن أثناء قبول SuperType للوسيطة من شأنه أن يفي بمبدأ الاستبدال ، لا يُسمح بذلك (هذا كما هو الحال في Java) هو أنه يمكنك أيضًا زيادة الحمل (عدة طرق مع نفسه الاسم ، عدد الوسائط المختلفة وأنواعها) وسيتم اعتبار طريقتك حمولة زائدة. أعتقد أن هذا هو مسألة توافق JVM بشكل أساسي ولديها مواصفات معقولة. التحميل الزائد بالفعل يجعل مواصفات Scala معقدة إلى حد ما. ببساطة توجيه طريقة التجاوز إلى طريقة التحميل الزائد مع التوقيع الذي تم تغييره قد يكون جيدًا بما فيه الكفاية:

class FooTest[A] extends Test[A] {
   override def test(a: Fasel) : Fasel = test(a.asInstanceOf[Bla])
   def test(a: Bla) : Fasel = new Fasel
}

ما يمكنك فعله هو جعل معلمة نوع المخالفات ، الواردة في الموضع المتناقض فقط (التبسيط ، يظهر كنوع وسيطة وليس كنوع النتيجة) ولكنه مختلف تمامًا:

trait Test[-A] {
  // note the - before A. 
  // You might want to constraint with -A >: Fasel
  def tester(a: A) : Bla = new Bla
}

class FooTest extends Test[Bla] {
  override def tester(a: Bla): Fasel = new Fasel
}

val testOfBla: Test[Bla] = new FooTest
val testOfFasel: Test[Fasel] = testOfBla 
  // you can assign a Test[Bla] to a test[Fasel] because of the -A

حسنًا في مثالك الثاني. توقيع tester() في Test يعلن أ Fasel الحجة ولكن مع توقيع التجاوز من FooTest tester() تم إعلانه مع أ Bla كحجة. حيث Fasel هو نوع فرعي من Bla بواستطهم extendS التسلسل الهرمي هذا خطأ ربما.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top