swift

swift - ¿Por qué este método con un parámetro opcional no invalida el método de clase base?



(3)

Este es un caso desafortunado. No hay manera de influir en la forma en que el compilador desambigua la llamada con Swift simple de esa manera.

Veamos algunas opciones:

  1. Reconoce lo que hace el compilador y anula BB.doTest() para reenviar la llamada a BB.doTest(_:)
  2. Use #selector(BB.doTest(_:)) para especificar la firma del método deseado
  3. Use los protocolos Swift para limitar los posibles métodos que el compilador está considerando

Reemplace el método base con nombre similar (probablemente la solución más simple)

La forma más fácil de forzar al compilador a no usar la firma del método más simple en este caso es anular la doTest en B y reencaminar la llamada:

class AA { func doTest() { print("a") } } class BB: AA { override func doTest() { self.doTest(true) } func doTest(_ different: Bool = true) { print("b") } } let bObjc = BB() bObjc.doTest()

Si eso no es posible, sigue leyendo.

Especifique el doTest(_:) (ObjC)

Puede especificar si desea llamar a doTest() o doTest(_:) través de selectores. Solo puede llamar a los métodos por sus selectores si anota el método como @objc y hace que el tipo sea heredado de NSObject , sin embargo. Así que esto podría ser una exageración en la práctica.

import Foundation class AA: NSObject { @objc func doTest() { print("a") } } class BB: AA { @objc func doTest(_ different: Bool = true) { print("b") } } let bObjc = BB() bObjc.performSelector(onMainThread: #selector(BB.doTest(_:)), with: nil, waitUntilDone: true)

Desambiguar utilizando protocolos.

Puede ayudar al compilador a elegir la solución correcta dividiendo el tipo donde se definen aún más doTest() y doTest(_:) . Use un protocolo y luego doTest el receptor del mensaje doTest al protocolo para que el compilador sepa que debe usar el método definido en el protocolo.

Paso 1: Extraer el protocolo.

El protocolo en sí:

protocol DoTestSpecific { func doTest(_ different: Bool) }

Desafortunadamente, no puede especificar valores de argumento predeterminados en definiciones de métodos en protocolos. Solo la implementación puede definir argumentos por defecto.

Paso 2: Usa el protocolo en la clase BB

La (bObjc as DoTestSpecific) le dirá al compilador que no considere AA.doTest en absoluto. Sin embargo, como el protocolo no especifica ningún parámetro predeterminado, debe proporcionar el valor en el sitio de la llamada por ahora:

class AA { func doTest() { print("a") } } class BB: AA, DoTestSpecific { func doTest(_ different: Bool = true) { print("b") } } let bObjc = BB() (bObjc as DoTestSpecific).doTest(false)

Paso 3: Mover la implementación a la extensión del protocolo

El resultado de la (bObjc as DoTestSpecific) no conoce la implementación de BB del método y el valor de parámetro predeterminado que lo acompaña.

¡Pero puede mover la implementación a una extensión de protocolo y así hacer una implementación que no requiera parámetros conocidos incluso para el resultado de la conversión!

Código final:

class AA { func doTest() { print("a") } } protocol DoTestSpecific { func doTest(_ different: Bool) } extension DoTestSpecific { func doTest(_ different: Bool = true) { print("b") } } class BB: AA, DoTestSpecific { } let bObjc = BB() (bObjc as DoTestSpecific).doTest()

Esto funciona como se esperaba. Requiere la adición de un protocolo y una implementación en una extensión de protocolo. Pero ahora ya no hay ambigüedad en el sitio de llamadas.

Si el código de ejemplo fuera más complejo e incluyera dependencias en el estado del objeto u otros objetos, probablemente sería mucho más difícil o incluso imposible hacer que esto funcionara.

El resultado es "a", pero quiero que sea "b". Quiero saber por qué y cómo puedo llamar a doTest sin argumentos para imprimir "b".

class AA { func doTest() { print("a") } } class BB: AA { func doTest(_ different: Bool = true) { print("b") } } let bObjc = BB() bObjc.doTest()


La clase BB no anula el método de AA , lo que significa que existen dos métodos en BB :

func doTest() // inherited func doTest(_ different: Bool = true) // declared

Cuando usted llama

bObjc.doTest()

El compilador tiene que elegir uno de ellos. Y prefiere el método sin parámetro sobre el método con un parámetro predeterminado.


La razón por la que obtiene una salida de a es que no está llamando a la nueva func doTest(_ different: Bool = true) su método func doTest(_ different: Bool = true) que espera un Bool .

bObjc.doTest(true) // b

Lo que realmente está haciendo es llamar al método doTest() que se hereda de la clase principal AA .

Si desea anular el método en AA , debe hacer precisamente eso:

class BB: AA { override func doTest() { print("b") } } let bObjc = BB() bObjc.doTest()