سؤال

لماذا هذه الطباعة WTF؟ هل مطابقة الأنماط لا تعمل على الأنواع الهيكلية؟

  "hello" match {
    case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
    case _ => println("okie dokie")
  }
هل كانت مفيدة؟

المحلول

تشغيل هذا المثال في مترجم سكالا مع تحذيرات غير محددة على (scala -unchecked) ينتج التحذير التالي: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure. لسوء الحظ ، لا يمكن التحقق من نوع عام مثل هذا في وقت التشغيل لأن JVM لا يحتوي على جيرلات عامة.

كل ما يراه JVM في هذا النمط هو:

"hello" match {
  case s: Object => ... 
  case annon: Object => ...
}

تعديل: ردا على تعليقاتك ، كنت أفكر في حل ولكن لم يكن لدي الوقت لنشره بالأمس. لسوء الحظ ، حتى لو ينبغي العمل ، فشل المترجم في ضخ ما Manifest.

المشكلة التي تريد حلها هي مقارنة ما إذا كان كائن من نوع هيكلي معين. إليك بعض التعليمات البرمجية التي كنت أفكر فيها (Scala 2.8-R20019 ، حيث تحطمت Scala 2.7.6.final لي عدة مرات أثناء اللعب بأفكار مماثلة)

type Foo = AnyRef { def doesNotExist(i: Int, x: List[_]): Double }

def getManifest[T](implicit m: Manifest[T]) = m

def isFoo[T](x: T)(implicit mt: Manifest[T]) = 
  mt == getManifest[Foo]

طريقة isFoo يقارن بشكل أساسي مظاهر الفصل x من Foo. في عالم مثالي ، يجب أن يكون بيان النوع الهيكلي مساوياً للبيان من أي نوع يحتوي على الطرق المطلوبة. على الأقل هذا هو قطار فكرتي. لسوء الحظ ، فشل هذا في التجميع ، حيث يقوم المترجم بحقن أ Manifest[AnyRef] بدل من Manifest[Foo] عند الاتصال getManifest[Foo]. ومن المثير للاهتمام ، إذا لم تستخدم نوعًا هيكليًا (على سبيل المثال ، type Foo = String) ، هذا الرمز يجمع ويعمل كما هو متوقع. سأقوم بنشر سؤال في مرحلة ما لمعرفة سبب فشل هذا في الأنواع الهيكلية - هل هو قرار تصميم ، أو أنه مجرد مشكلة في واجهة برمجة تطبيقات الانعكاس التجريبي.

إذا فشل ذلك ، يمكنك دائمًا استخدام انعكاس Java لمعرفة ما إذا كان الكائن يحتوي على طريقة.

def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = {
  try { 
    x.getClass.getMethod(name, params: _*)
    true
    }
  catch {
    case _ =>  false
  }
}

الذي يعمل كما هو متوقع:

containsMethod("foo", "concat", classOf[String]) // true
containsMethod("foo", "bar", classOf[List[Int]]) // false

... لكنها ليست لطيفة للغاية.

لاحظ أيضًا أن هيكل النوع الهيكلي غير متوفر في وقت التشغيل. إذا كان لديك طريقة def foo(x: {def foo: Int}) = x.foo, ، بعد محو ستحصل عليه def foo(x: Object) = [some reflection invoking foo on x], ، نوع المعلومات التي تضيع. لهذا السبب يتم استخدام الانعكاس في المقام الأول ، حيث يجب عليك استدعاء طريقة على Object و JVM لا يعرف ما إذا كان Object لديه هذه الطريقة.

نصائح أخرى

إذا كنت ستضطر إلى استخدام التفكير ، فيمكنك على الأقل جعلها تبدو أجمل مع مستخرج:

object WithFoo {
    def foo(){
        println("foo was called")
    }
}

object HasFoo {
    def containsMethod(x: AnyRef, name: String, params: Array[java.lang.Class[_]]) : Boolean = {
        try { 
            x.getClass.getMethod(name, params: _*)
            true
        } catch {
            case _ => false
        }
    }

    def unapply(foo:AnyRef):Option[{def foo():Unit}] = {
        if (containsMethod(foo, "foo", new Array[Class[_]](0))) {
            Some(foo.asInstanceOf[{def foo():Unit}])
        } else None
    }
}


WithFoo.asInstanceOf[AnyRef] match {
    case HasFoo(foo) => foo.foo()
    case _ => println("no foo")
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top