Diferencia exacta entre CharSequence y String en java

Leí esta publicación anterior . ¿Puede alguien decir cuál es la diferencia exacta entre CharSequence y String, aparte del hecho de que String implementa CharSequence y que String es una secuencia de caracteres? Por ejemplo:

 CharSequence obj = "hello"; String str = "hello"; System.out.println("output is : " + obj + " " + str); 

¿Qué sucede cuando se asigna “hello” a obj y de nuevo a str ?

Diferencias generales

Hay varias clases que implementan la interfaz CharSequence además de String . Entre estos están

  • StringBuilder para secuencias de caracteres de longitud variable que pueden modificarse
  • CharBuffer para secuencias de caracteres de bajo nivel de longitud fija que pueden modificarse

Cualquier método que acepte una CharSequence puede operar en todos ellos igualmente bien. Cualquier método que solo acepte una String requerirá la conversión. Así que usar CharSequence como un tipo de argumento en todos los lugares donde no te importan las partes internas es prudente. Sin embargo, debe usar String como un tipo de retorno si realmente devuelve un String , porque eso evita posibles conversiones de valores devueltos si el método de llamada realmente requiere un String .

También tenga en cuenta que los mapas deben utilizar String como tipo de clave, no CharSequence , ya que las claves del mapa no deben cambiar. En otras palabras, a veces la naturaleza inmutable de String es esencial.

Fragmento de código específico

En cuanto al código que pegó: simplemente compile eso y eche un vistazo al bytecode de JVM usando javap -v . Allí notarás que tanto obj como str son referencias al mismo objeto constante. Como una String es inmutable, este tipo de intercambio está bien.

El operador + de String se comstack como invocaciones de varias llamadas StringBuilder.append . Entonces es equivalente a

 System.out.println( (new StringBuilder()) .append("output is : ") .append((Object)obj) .append(" ") .append(str) .toString() ) 

Debo confesar que estoy un poco sorprendido de que mi comstackdor javac 1.6.0_33 el + obj usando StringBuilder.append(Object) lugar de StringBuilder.append(CharSequence) . El primero probablemente implica una llamada al método toString() del objeto, mientras que el último debería ser posible de una manera más eficiente. Por otro lado, String.toString() simplemente devuelve el String mismo, por lo que hay poca penalización allí. Así que StringBuilder.append(String) podría ser más eficiente por aproximadamente una invocación de método.

tl; dr

Una es una interfaz ( CharSequence ) mientras que otra es una implementación concreta de esa interfaz ( String ).

 CharSequence animal = "cat" // `String` object presented as the interface `CharSequence`. 

Como interfaz, normalmente la CharSequence se vería más comúnmente que String , pero algunos antecedentes retorcidos dieron como resultado que la interfaz se definiera años después de la implementación. Por lo tanto, en las API más antiguas, a menudo vemos String mientras que en API más nuevas, tendemos a ver a CharSequence para definir argumentos y tipos de devolución.

Detalles

Hoy en día, sabemos que generalmente una API / framework debe enfocarse en exportar interfaces principalmente y en clases concretas de manera secundaria. Pero no siempre supimos esta lección tan bien.

La clase String fue primero en Java. Solo más tarde CharSequence interfaz CharSequence , CharSequence .

Historia retorcida

Un poco de historia puede ayudar con la comprensión.

En sus comienzos, Java se apresuró a comercializar un poco antes de tiempo, debido a la manía de Internet / Web que anima la industria. Algunas bibliotecas no fueron tan bien pensadas como deberían haber sido. El manejo de cadenas fue una de esas áreas.

Además, Java fue uno de los primeros entornos no académicos orientados a la producción orientados a objetos (OOP) . Las únicas implementaciones exitosas de OOP en el mundo real antes de eso fueron algunas versiones limitadas de SmallTalk , luego Objective-C con NeXTSTEP / OpenStep . Por lo tanto, muchas lecciones prácticas aún no se habían aprendido.

Java comenzó con la clase String y la clase StringBuffer . Pero esas dos clases no estaban relacionadas, no estaban unidas entre sí por herencia o interfaz. Más tarde, el equipo de Java reconoció que debería haber existido un vínculo unificador entre las implementaciones relacionadas con cadenas para hacerlas intercambiables. En Java 4, el equipo agregó la interfaz CharSequence e implementó de forma retroactiva esa interfaz en el String and String Buffer, además de agregar otra implementación CharBuffer . Más tarde en Java 5 agregaron StringBuilder , básicamente una versión no sincronizada y, por lo tanto, algo más rápida de StringBuffer .

Entonces estas clases orientadas a cuerdas son un poco desordenadas, y un poco confusas de aprender. Se crearon muchas bibliotecas e interfaces para tomar y devolver objetos String . Hoy en día, tales bibliotecas generalmente deben construirse para esperar CharSequence . Pero (a) String parece seguir dominando el espacio mental, y (b) puede haber algunos problemas técnicos sutiles al mezclar las diversas implementaciones de CharSequence . Con la visión 20/20 de retrospectiva, podemos ver que todo este material de cuerda podría haberse manejado mejor, pero aquí estamos.

Idealmente, Java habría comenzado con una interfaz y / o superclase que se usaría en muchos lugares donde ahora usamos String , del mismo modo que utilizamos las interfaces Collection o List en lugar de las implementaciones ArrayList o LinkedList .

Interfaz frente a clase

La diferencia clave sobre CharSequence es que es una interfaz , no una implementación . Eso significa que no puedes instanciar directamente una CharSequence . Más bien, crea una de las clases que implementa esa interfaz.

Por ejemplo, aquí tenemos x que se parece a una CharSequence pero debajo de esto, en realidad, se trata de un objeto StringBuilder .

 CharSequence x = new StringBuilder( "dog" ); 

Esto se vuelve menos obvio cuando se utiliza un literal String. Tenga en cuenta que cuando ve el código fuente con solo comillas alrededor de los caracteres, el comstackdor lo está traduciendo en un objeto String.

 CharSequence y = "cat"; // Looks like a CharSequence but is actually a String instance. 

Hay algunas diferencias sutiles entre "cat" y la new String("cat") como se discutió en esta otra Pregunta , pero son irrelevantes aquí.

Diagtwig de clase

Este diagtwig de clase puede ayudar a guiarlo. Anoté la versión de Java en la que parecían demostrar cuánto cambio se ha producido a través de estas clases e interfaces.

Diagrama que muestra las diversas clases e interfaces relacionadas con cadenas a partir de Java 8

Literales de cadena sin procesar

Una versión futura de Java puede obtener la nueva característica de los literales de cadena sin formato . Esto haría que escribir cadenas de código incrustadas como SQL sea más conveniente. Ver JEP 326 .

Este JEP propone un nuevo tipo de literal, un literal de cadena sin procesar, que deja de lado las escapadas de Java y las especificaciones del terminador de línea Java, para proporcionar secuencias de caracteres que en muchas circunstancias son más legibles y mantenibles que el literal de cadena tradicional existente.

Para obtener más información, consulte esta publicación y vea esta publicación de Goetz, literales de cadenas sin formato: dónde estamos y cómo llegamos aquí .

CharSequence es un contrato ( interfaz ) y String es una implementación de este contrato.

 public final class String extends Object implements Serializable, Comparable, CharSequence 

La documentación para CharSequence es:

Una CharSequence es una secuencia legible de valores de char. Esta interfaz proporciona acceso uniforme y de solo lectura a muchos tipos diferentes de secuencias de char. Un valor de char representa un personaje en el plano multilingüe básico (BMP) o un suplente. Consulte Representación de caracteres de Unicode para más detalles.

aparte del hecho de que String implementa CharSequence y que String es una secuencia de caracteres.

Varias cosas suceden en tu código:

 CharSequence obj = "hello"; 

Eso crea un literal de String , "hello" , que es un objeto String . Al ser un String , que implementa CharSequence , también es una CharSequence . (Puede leer esta publicación sobre encoding en la interfaz, por ejemplo).

La siguiente línea:

 String str = "hello"; 

es un poco mas complejo String literales de String en Java se guardan en un grupo (interno) de modo que el "hello" en esta línea es el mismo objeto (identidad) que el "hello" en la primera línea. Por lo tanto, esta línea solo asigna el mismo literal de str a str .

En este punto, tanto obj como str son referencias al String literal "hello" y por lo tanto son equals , == y son a la vez un String y un CharSequence .

Sugiero que pruebes este código, mostrando en acción lo que acabo de escribir:

 public static void main(String[] args) { CharSequence obj = "hello"; String str = "hello"; System.out.println("Type of obj: " + obj.getClass().getSimpleName()); System.out.println("Type of str: " + str.getClass().getSimpleName()); System.out.println("Value of obj: " + obj); System.out.println("Value of str: " + str); System.out.println("Is obj a String? " + (obj instanceof String)); System.out.println("Is obj a CharSequence? " + (obj instanceof CharSequence)); System.out.println("Is str a String? " + (str instanceof String)); System.out.println("Is str a CharSequence? " + (str instanceof CharSequence)); System.out.println("Is \"hello\" a String? " + ("hello" instanceof String)); System.out.println("Is \"hello\" a CharSequence? " + ("hello" instanceof CharSequence)); System.out.println("str.equals(obj)? " + str.equals(obj)); System.out.println("(str == obj)? " + (str == obj)); } 

Sé que es algo obvio, pero CharSequence es una interfaz, mientras que String es una clase concreta 🙂

java.lang.String es una implementación de esta interfaz …

Considera UTF-8. En UTF-8, los puntos de código Unicode se crean a partir de uno o más bytes. Una clase que encapsula una matriz de bytes UTF-8 puede implementar la interfaz CharSequence, pero definitivamente no es una cadena. Ciertamente, no puede pasar una matriz de bytes UTF-8 donde se espera una Cadena, pero ciertamente puede aprobar una clase de contenedor UTF-8 que implemente CharSequence cuando el contrato se relaje para permitir una CharSequence. En mi proyecto, estoy desarrollando una clase llamada CBTF8Field (formato de transferencia binaria comprimida – Ocho bits) para proporcionar compresión de datos para xml y estoy buscando usar la interfaz CharSequence para implementar conversiones desde matrices de bytes CBTF8 hacia / desde matrices de caracteres (UTF-16 ) y matrices de bytes (UTF-8).

La razón por la que vine aquí fue para obtener una comprensión completa del contrato posterior.

De la API de Java de CharSequence :

Una CharSequence es una secuencia legible de caracteres. Esta interfaz proporciona acceso uniforme y de solo lectura a muchos tipos diferentes de secuencias de caracteres.

Esta interfaz es luego utilizada por String , CharBuffer y StringBuffer para mantener la consistencia de todos los nombres de métodos.

En charSequence no tienes métodos muy útiles que estén disponibles para String. Si no desea buscar en la documentación, escriba: obj. y str.

y vea qué métodos le ofrece su comstackdor. Esa es la diferencia básica para mí.

    Intereting Posts