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:
-
Reconoce lo que hace el compilador y anula
BB.doTest()
para reenviar la llamada aBB.doTest(_:)
-
Use
#selector(BB.doTest(_:))
para especificar la firma del método deseado - 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()