2011-09-05 12 views
47

Deseo saber si hay alguna manera en la que pueda obligar a la clase de implementador a declarar los objetos identificadores/primitivos como lo hacen con los métodos. por ej .:Atributos/variables miembro en interfaces?

public interface Rectangle {  
    int height = 0; 
    int width = 0; 

    public int getHeight(); 
    public int getWidth(); 
    public void setHeight(int height); 
    public void setWidth(int width);     
} 


public class Tile implements Rectangle{ 
    @Override 
    public int getHeight() { 
     return 0; 
    } 

    @Override 
    public int getWidth() { 
     return 0; 
    } 

    @Override 
    public void setHeight(int height) { 
    } 

    @Override 
    public void setWidth(int width) { 
    } 

} 

En el método anterior, ¿cómo podemos obligar a la clase del azulejo para declarar la altura y la anchura de los atributos utilizando la interfaz? Por alguna razón, deseo hacerlo solo con la interfaz.

Inicialmente pensé en usarlo con herencia. Pero la cosa es que tengo que lidiar con 3 clases.!

  1. rectángulo
  2. Teja
  3. JLabel.!

 

class Tile extends JLabel implements Rectangle {} 

funcionaría.!

pero

class Tile extends JLabel extends Rectangle {} 

no woud.!

+4

Su interfaz es incorrecta. No puede contener atributos. ¿Tal vez quieres usar una clase abstracta? – pvoosten

+5

@lbp Eso [compilará realmente] (http://www.coderanch.com/t/178630/java-SCJA/certification/Instance-variables-interface) ;-) Es simplemente ... no el "esperado" comportamiento. El compilador asume/aplica un modificador 'static final'. –

+0

Puede usar Height en lugar de Hieght and Hight. Hight es el participio pasado de highten, hihten, para llamar, ser llamado.;) –

Respuesta

52

El objetivo de una interfaz es especificar la API pública. Una interfaz no tiene estado. Las variables que crea son realmente constantes (por lo tanto, tenga cuidado al crear objetos mutables en las interfaces).

Básicamente una interfaz dice que aquí están todos los métodos que una clase que implementa debe admitir. Probablemente hubiera sido mejor si los creadores de Java no hubieran permitido las constantes en las interfaces, pero es demasiado tarde para deshacerse de eso ahora (y hay algunos casos en los que las constantes son sensibles en las interfaces).

Como solo está especificando qué métodos deben implementarse, no hay idea de estado (no hay variables de instancia). Si desea requerir que cada clase tenga una determinada variable, debe usar una clase abstracta.

Por último, en general, no debe usar variables públicas, por lo que la idea de poner variables en una interfaz es una mala idea para empezar.

Respuesta breve: no puede hacer lo que quiere porque está "mal" en Java.

Editar:

class Tile 
    implements Rectangle 
{ 
    private int height; 
    private int width; 

    @Override 
    public int getHeight() { 
     return height; 
    } 

    @Override 
    public int getWidth() { 
     return width; 
    } 

    @Override 
    public void setHeight(int h) { 
     height = h; 
    } 

    @Override 
    public void setWidth(int w) { 
     width = w; 
    } 
} 

una versión alternativa sería:

abstract class AbstractRectangle 
    implements Rectangle 
{ 
    private int height; 
    private int width; 

    @Override 
    public int getHeight() { 
     return height; 
    } 

    @Override 
    public int getWidth() { 
     return width; 
    } 

    @Override 
    public void setHeight(int h) { 
     height = h; 
    } 

    @Override 
    public void setWidth(int w) { 
     width = w; 
    } 
} 

class Tile 
    extends AbstractRectangle 
{ 
} 
+0

kk! Entonces, ¿cómo lo hago? (He editado la pregunta, pls check)! – Shrey

+0

Esto ayudará, supongo ...! thnxx :) – Shrey

+0

Pero eso deja el problema de que no se puede meter con la altura en la implementación concreta. Entonces sabes que necesitas una altura, sabes que necesitas poder devolver la altura, pero no puedes dejar que cada implementación calcule la altura (lo cual creo que frustra el objetivo). Por supuesto, puede declarar 'getHeight' abstracto y no declarar la altura en absoluto, pero luego debe repetir el código de retorno cada vez, solo para que pueda mantener la variable privada. –

6

Las interfaces no pueden requieren variables de instancia para definir - solo métodos.

(Variables can be defined in interfaces, pero no se comportan como era de esperar:. Son tratados como final static)

feliz de codificación.

7

Solo puede hacer esto con una clase abstracta, no con una interfaz.

Declare Rectangle como un abstract class en lugar de un interface y declarar los métodos que deben ser implementados por la sub-clase como public abstract. Luego la clase Tile extiende la clase Rectangle y debe implementar los métodos abstractos de Rectangle.

+0

Inicialmente pensé en usarlo con herencia. Pero la cosa es que tengo que lidiar con 3 clases.! 1. Rectángulo 2. Mosaico 3. JLabel.! clase Tile extends JLabel implementa Rectangle {} funcionaría.! pero clase Tile extends JLabel extends Rectangle {} woud not.! – Shrey

2

En Java no puede. La interfaz tiene que ver con los métodos y la firma, no tiene que ver con el estado interno de un objeto, es una pregunta de implementación. Y esto tiene sentido también. Es decir, simplemente porque existen ciertos atributos, no significa que tengan que ser utilizados por la clase implementadora. getHeight podría apuntar a la variable ancho (suponiendo que el implementador es un sádico).

(Como nota - esto no es cierto en todos los idiomas, ActionScript permite la declaración de atributos seudo, y creo que también lo hace C#)

+1

En C# * getters/setters * (que en realidad son solo llamadas a métodos especiales) puede estar en interfaces, las variables de instancia no pueden hacerlo. –

+0

Eso es lo que pensé (y es por eso que dije pseudo) que ActionScript hace lo mismo. – cwallenpoole

+0

kk !! herencia en lugar de implementación ayudaría.! ¡Pero esto me plantearía un nuevo problema ...! ¿Puedes sugerirme la forma de hacerlo? (ver la pregunta editada) ¡¡¡zas !! – Shrey

0

campos en las interfaces son implícitamente public static final. (Además, los métodos son implícitamente públicos, por lo que puede soltar la palabra clave public). Incluso si utiliza una clase abstracta en lugar de una interfaz, le sugiero hacer todas las no constantes (public static final de una referencia de objeto primitivo o inmutable) private. En términos más generales, "prefiera la composición a la herencia": Tile is-not-a Rectangle (por supuesto, puede jugar juegos de palabras con "is-a" y "has-a").

+0

Shape of Tile is-a Rectangle .. ?? (is-a y has-a me confunde más) – Shrey

+0

Supongo que A debería extender B si y solo si la respuesta a 'es cada B a A' es verdadera ...! Lea de Effective Java .. – Shrey

+1

¿Cómo puede B ser una A si A extiende B? Lo tienes de vuelta al frente. – RichieHH

0

Algo importante ha sido dicho por Tom:

si utiliza la cuenta, un concepto, se evitar el problema

En efecto, si en lugar de utilizar extiende y implementos definir dos atributos, uno de tipo rectángulo, uno de tipo JLabel en su clase Tile, a continuación, se puede definir un Rectangle a ser una interfaz o una clase.

Además, normalmente recomendaría el uso de interfaces en relación con has-a, pero supongo que sería excesivo en su situación. Sin embargo, usted es el único que puede decidir sobre este punto (flexibilidad de intercambio/sobreingeniería).

4

Java 8 introdujo default methods para las interfaces con las cuales puede agrupar los métodos. De acuerdo con las OOP, las interfaces deben actuar como un contrato entre dos sistemas/partes.

Pero aún así encontré una manera de lograr el almacenamiento de propiedades en la interfaz. Admito que es una implementación algo fea.

import java.util.Map; 
    import java.util.WeakHashMap; 

interface Rectangle 
{ 

class Storage 
{ 
    private static final Map<Rectangle, Integer> heightMap = new WeakHashMap<>(); 
    private static final Map<Rectangle, Integer> widthMap = new WeakHashMap<>(); 
} 

default public int getHeight() 
{ 
    return Storage.heightMap.get(this); 
} 

default public int getWidth() 
{ 
    return Storage.widthMap.get(this); 
} 

default public void setHeight(int height) 
{ 
    Storage.heightMap.put(this, height); 
} 

default public void setWidth(int width) 
{ 
    Storage.widthMap.put(this, width); 
} 
} 

Esta interfaz es fea. Para almacenar propiedades simples, necesitaba dos hashmaps y cada hashmap crea de forma predeterminada 16 entradas de forma predeterminada. Además, cuando se desreferencia al objeto real, la JVM también necesita eliminar esta referencia débil.

+0

Bastante interesante, pero extraño :). Incluso puede eliminar la clase interna y simplemente almacenar mapas finales estáticos en su interfaz. – iMysak