Los modificadores de acceso en AS2 eran bastante claros: una variable o un método solo podían ser públicos o privados. Otra cosa es que luego fueran realmente privados o no, pero eso es otra historia.
Uno de los cambios en la estructura de las clases AS3 es la existencia de un idiom para los modificadores de acceso más cercano al de otros lenguajes, como Java, incluyendo dos nuevos modificadores: internal y protected.
internal
Es el modificador por defecto. Los métodos y variables de una clase declarados como internal son accesibles a todo el package al que pertenece esa clase.
Para comprobarlo, construye una clase con un método internal:
package internalTest
{
import flash.util.trace
public class HasInternalMethod
{
function HasInternalMethod( )
{
trace( "HasInternalMethod.constructor" );
}
internal function internalMethod( )
{
trace( "HasInternalMethod.internalMethod( )" );
}
}
}
A continuación, en el mismo package, otra clase con un método público, que instancie la clase anterior, y que intente acceder al método internal de esa instancia:
package internalTest
{
import flash.util.trace
public class HasPublicMethod
{
function HasPublicMethod( )
{
trace( "HasPublicMethod.constructor" );
}
public function publicMethod( )
{
var internalInstance: HasInternalMethod = new HasInternalMethod( );
internalInstance.internalMethod( );
}
}
}
A continuación, en la línea de tiempo principal, puedes comprobar que no se puede acceder directamente al método internal:
import internalTest.HasInternalMethod
var instance: HasInternalMethod = new HasInternalMethod( );
instance.internalMethod( );
Pero sí a través del método público de la segunda clase:
import internalTest.HasPublicMethod
var instance: HasPublicMethod = new HasPublicMethod( );
instance.publicMethod( );
protected
Los métodos y variables de una clase declarados como protected sólo son accesibles a las subclases. No al resto del paquete, ni a los públicos.
Sin embargo, un método declarado como protected también será visible para subclases de la original que no estén en el mismo paquete.
Para comprobarlo, declara una clase con un método protected:
package protectedTest
{
import flash.util.trace
public class HasProtectedMethod
{
function HasProtectedMethod( )
{
trace( "HasProtectedMethod.constructor" );
}
internal function internalMethod( )
{
trace( "HasProtectedMethod.internalMethod( )" );
}
protected function superMethod( )
{
trace( "HasProtectedMethod.superMethod" );
}
}
}
Y una subclase de ésta, que intente atacar a ese método protected de la superclase:
package protectedTest
{
public class SubClass extends HasProtectedMethod
{
function SubClass( )
{
trace( "Subclass.constructor" );
}
public function init( )
{
//Método internal de la superclase
internalMethod( );
//Método protected de la superclase
superMethod( );
}
}
}
A continuación, crea una instancia de la subclase en la línea de tiempo principal:
import protectedTest.*
var instance: SubClass = new SubClass( );
instance.init( );
Sin embargo, no puedes acceder directamente al método de la superclase desde fuera del package:
var superInstance: HasProtectedMethod = new HasProtectedMethod( );
superInstance.superMethod( );
Eso generará el correspondiente error de compilación.
private
Private, ahora, quiere decir "totalmente privado". Los métodos y variables declarados como privados no pueden ser accesibles ni por otras clases del mismo package, ni siquiera por las subclases.
Declara una clase con un método privado:
package privateTest
{
import flash.util.trace
public class HasPrivateMethod
{
function HasPrivateMethod( )
{
trace( "HasPrivateMethod.constructor" );
}
internal function internalMethod( )
{
trace( "HasPrivateMethod.internalMethod( )" );
}
protected function superMethod( )
{
trace( "HasPrivateMethod.superMethod" );
}
private function privateMethod( )
{
trace( "HasPrivateMethod.privateMethod" );
}
}
}
A continuación, una subclase de ésta, que intente lanzar un mensaje al método privado de la superclase:
package privateTest
{
public class SubClass extends HasPrivateMethod
{
function SubClass( )
{
trace( "Subclass.constructor" );
}
public function init( )
{
//Método internal de la superclase
internalMethod( );
//Método protected de la superclase
superMethod( );
//Método private de la superclase. Error de compilación
privateMethod( );
}
}
}
A continuación, desde la línea de tiempo principal, lanza el ejemplo:
import privateTest.*
var instance: SubClass = new SubClass( );
instance.init( );
El uso consciente de private y protected permite un control muy fino sobre el encapsulamiento de las superclases. De esa forma, uno de los problemas de la herencia, que era la exposición de la superclase a las subclases puede resolverse declarando métodos como privados.
override
No es posible sobrescribir variables declaradas en una superclase, sean var o const. Sin embargo, sí se pueden sobrescribir métodos. No obstante, sólo se pueden sobrescribir métodos que no sean estáticos (ya que los estáticos no se heredan), que no se hayan declarado como final, y que no se hayan declarado como private.
Por ejemplo, la superclase podría ser:
package overrideTest
{
import flash.util.trace
public class HasOverrideMethod
{
function HasOverrideMethod( )
{
trace( "HasOverrideMethod.constructor" );
}
internal function internalMethod( )
{
trace( "HasOverrideMethod.internalMethod( )" );
}
protected function superMethod( )
{
trace( "HasOverrideMethod.superMethod" );
}
private function privateMethod( )
{
trace( "HasFinalMethod.privateMethod" );
}
final function finalMethod( )
{
trace( "HasOverrideMethod.finalMethod" );
}
protected function toOverride( itemCount: Number, address: String ): String
{
return "";
}
}
}
Y la subclase:
package overrideTest
{
public class SubClass extends HasOverrideMethod
{
function SubClass( )
{
trace( "Subclass.constructor" );
}
public function init( )
{
//Método internal de la superclase
internalMethod( );
//Método protected de la superclase
superMethod( );
//Método private de la superclase
//privateMethod( );
}
//No compila, distinto tipo de retorno
/*
override protected function toOverride( ): Number
{
return 2;
}
*/
//No compila, distinto número de parámetros
/*
override protected function toOverride( ): String
{
return "";
}
*/
override protected function toOverride( itemCount: Number, address: String ): String
{
return "";
}
//Este método ocupa el lugar del privado de la superclase
function privateMethod( )
{
trace( "método privado de la subclase" );
}
}
}
Y para realizar la comprobación:
import overrideTest.*
var instance: SubClass = new SubClass( );
instance.init( );
Hay que tener en cuenta que el método de la subclase debe tener los mismos modificadores de acceso que el de la superclase, el mismo número de parámetros y del mismo tipo, y si devuelve algún valor, debe ser del mismo tipo que el de la clase base. Por ejemplo, si el método de la superclase se declara como protected, el sobrescrito no puede declararse como internal
Si en la superclase se declara algún método como private, al no estar éste disponible para las subclases, se podrían declarar en las subclases métodos con el mismo nombre, sin peligro de colisión, como se puede ver en el ejemplo anterior.
final
La posibilidad de declarar métodos como final es nueva para ActionScript, pero no para muchos otros lenguajes.
Puedes declarar una clase con un método final:
package finalTest
{
import flash.util.trace
public class HasFinalMethod
{
function HasFinalMethod( )
{
trace( "HasFinalMethod.constructor" );
}
internal function internalMethod( )
{
trace( "HasFinalMethod.internalMethod( )" );
}
protected function superMethod( )
{
trace( "HasPrivateMethod.superMethod" );
}
private function privateMethod( )
{
trace( "HasFinalMethod.privateMethod" );
}
final function finalMethod( )
{
trace( "HasFinalMethod.finalMethod" );
}
}
}
E intentar sobreescribirlo en una subclase:
package finalTest
{
public class SubClass extends HasFinalMethod
{
function SubClass( )
{
trace( "Subclass.constructor" );
}
public function init( )
{
//Método internal de la superclase
internalMethod( );
//Método protected de la superclase
superMethod( );
//Método private de la superclase
//privateMethod( );
//Método final de la superclase
finalMethod( );
}
override final function finalMethod( )
{
}
}
}
Con el correspondiente error de compilación:
import finalTest.*
var instance: SubClass = new SubClass( );
instance.init( );
Las clases también pueden declararse como final para, de esa forma, evitar que puedan ser extendidas:
package finalTest
{
import flash.util.trace
public final class FinalClass
{
function FinalClass( )
{
trace( "FinalClass.constructor" );
}
}
}
Y su subclase:
package finalTest
{
import flash.util.trace
public class FinalSubClass extends FinalClass
{
function FinalSubClass( )
{
trace( "FinalSubClass.constructor" );
}
}
}
Y comprobar que no se puede compilar:
import finalTest.*
var instance: FinalSubClass = new FinalSubClass( );
dynamic
Las clases declaradas como dynamic pueden ser modificadas en tiempo de ejecución, se las puede añadir métodos y propiedades. En realidad esto no es una novedad en sí, la verdadera novedad está en que ya no es el comportamiento por defecto
La clase dinámica más sencilla posible seria algo como:
package dynamicTest
{
public dynamic class DynamicClass
{
function DynamicClass( )
{
}
}
}
Se puede comprobar que es posible crear propiedades de esa clase en tiempo de ejecución:
import dynamicTest.*
var instance: DynamicClass = new DynamicClass( );
instance[ "var1" ] = 24;
trace( "valor obtenido " + instance.var1 );
En el próximo post ya abandonaremos el terreno de lo abstracto y empezaremos a construir una prueba de concepto, más que aplicación, basada en el modelo-vista-controlador.
El código de los ejemplos puede descargarse aquí.