Este equipo especializado pasados muchos días de investigación y recopilar de datos, hallamos la solución, nuestro deseo es que resulte útil para ti en tu plan.
Solución:
Diferencias generales
Hay varias clases que implementan la CharSequence
interfaz además String
. Entre estos se encuentran
StringBuilder
para secuencias de caracteres de longitud variable que se pueden modificarCharBuffer
para secuencias de caracteres de bajo nivel de longitud fija que se pueden modificar
Cualquier método que acepte un CharSequence
puede operar en todos estos igualmente bien. Cualquier método que solo acepte un String
requerirá conversión. Entonces usando CharSequence
como tipo de argumento en todos los lugares donde no le importan los aspectos internos es prudente. Sin embargo debes 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 usar 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 compílelo y eche un vistazo al código de bytes de JVM usando javap -v
. Allí notarás que ambos obj
y str
son referencias al mismo objeto constante. Como un String
es inmutable, este tipo de compartir está bien.
los +
operador de String
se compila como invocaciones de varios StringBuilder.append
llamadas. 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 compilador javac 1.6.0_33
compila el + obj
utilizando StringBuilder.append(Object)
en lugar de StringBuilder.append(CharSequence)
. El primero probablemente implica una llamada al toString()
método del objeto, mientras que este último debería ser posible de una manera más eficiente. Por otra parte, String.toString()
simplemente devuelve el String
en sí mismo, por lo que hay poca penalización allí. Entonces StringBuilder.append(String)
podría ser más eficiente mediante la invocación de un método.
tl; dr
Uno es una interfaz (CharSequence
) mientras que otro es una implementación concreta de esa interfaz (String
).
CharSequence animal = "cat" // `String` object presented as the interface `CharSequence`.
Al igual que ArrayList
es un List
, y HashMap
es un Map
, así también String
es un CharSequence
.
Como interfaz, normalmente el CharSequence
sería más común que String
, pero una historia retorcida dio como resultado que la interfaz se definiera años después la implementación. Por eso, en las API más antiguas, a menudo vemos String
mientras que en las API más nuevas tendemos a ver CharSequence
utilizado para definir argumentos y tipos de retorno.
Detalles
Hoy en día sabemos que, en general, una API / framework debe enfocarse en exportar interfaces principalmente y clases concretas en segundo lugar. Pero no siempre conocimos tan bien esta lección.
los String
la clase fue la primera en Java. Solo más tarde colocaron una interfaz frontal, CharSequence
.
Historia retorcida
Un poco de historia podría ayudar a comprenderlo.
En sus inicios, Java se apresuró a comercializar un poco antes de su tiempo, debido a la manía de Internet / Web que animaba a la industria. Algunas bibliotecas no estaban tan bien pensadas como deberían. El manejo de cuerdas fue una de esas áreas.
Además, Java fue uno de los primeros entornos de programación orientada a objetos (OOP) no académicos y orientados a la producción. 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, aún quedaban por aprender muchas lecciones prácticas.
Java comenzó con el String
clase y StringBuffer
clase. Pero esas dos clases no estaban relacionadas, no estaban vinculadas entre sí por herencia ni interfaz. Más tarde, el equipo de Java reconoció que debería haber habido un vínculo unificador entre stringimplementaciones relacionadas para hacerlas intercambiables. En Java 4, el equipo agregó el CharSequence
e implementó retroactivamente esa interfaz en String y 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 estos string-Las clases orientadas son un poco desordenadas y un poco confuso aprender sobre ellas. Muchas bibliotecas e interfaces se crearon para aceptar y devolver String
objetos. Hoy en día, estas bibliotecas deberían construirse para esperar CharSequence
. Pero (a) String
parece dominar todavía el espacio mental, y (b) puede haber algunos problemas técnicos sutiles al mezclar los diversos CharSequence
implementaciones. Con la visión 20/20 en retrospectiva, podemos ver que todo esto string las cosas podrían 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
, al igual que usamos el Collection
o List
interfaces en lugar de ArrayList
o LinkedList
implementaciones.
Interfaz versus clase
La diferencia clave sobre CharSequence
es que es una interfaz, no una implementación. Eso significa que no puede instanciar directamente un CharSequence
. En su lugar, crea una instancia de una de las clases que implementa esa interfaz.
Por ejemplo, aquí tenemos x
que parece un CharSequence
pero debajo hay en realidad un StringBuilder
objeto.
CharSequence x = new StringBuilder( "dog" ); // Looks like a `CharSequence` but is actually a `StringBuilder` instance.
Esto se vuelve menos obvio cuando se usa un literal de cadena. Tenga en cuenta que cuando ve el código fuente con solo comillas alrededor de los caracteres, el compilador lo está traduciendo a un objeto String.
CharSequence y = "cat"; // Looks like a `CharSequence` but is actually a `String` instance.
Literal versus constructor
Hay algunas diferencias sutiles entre "cat"
y new String("cat")
como se discutió en esta otra Pregunta, pero son irrelevantes aquí.
Diagrama de clase
Este diagrama de clases puede servir de guía. Observé 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.
Bloques de texto
Aparte de agregar más caracteres Unicode, incluida una multitud de emoji, en los últimos años no ha cambiado mucho en Java para trabajar con texto. Hasta que bloques de texto.
Los bloques de texto son una nueva forma de manejar mejor el tedio de string literales con varias líneas o caracteres de escape. Esto haría que escribir cadenas de código incrustadas como HTML, XML, SQL o JSON sea mucho más conveniente.
Para citar JEP 378:
Un bloque de texto es un multilínea string literal que evita la necesidad de la mayoría de las secuencias de escape, formatea automáticamente el string de una manera predecible, y le da al desarrollador control sobre el formato cuando lo desee.
La función de bloques de texto no no introducir un nuevo tipo de datos. Los bloques de texto son simplemente una nueva sintaxis para escribir un String
literal. Un bloque de texto produce un String
objeto, al igual que la sintaxis literal convencional. Un bloque de texto produce un String
objeto, que también es un CharSequence
objeto, como se discutió anteriormente.
Ejemplo de SQL
Para citar JSR 378 nuevamente …
Usando “unidimensional” string literales.
String query = "SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"n" +
"WHERE "CITY" = 'INDIANAPOLIS'n" +
"ORDER BY "EMP_ID", "LAST_NAME";n";
Usar un bloque de texto “bidimensional”
String query = """
SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
WHERE "CITY" = 'INDIANAPOLIS'
ORDER BY "EMP_ID", "LAST_NAME";
""";
Los bloques de texto se encuentran en Java 15 y posteriores, por JEP 378: Bloques de texto.
Primera vista previa en Java 13, bajo JEP 355: Bloques de texto (vista previa). Luego, vista de nuevo en Java 14 en JEP 368: Bloques de texto (segunda vista previa).
Este esfuerzo fue precedido por JEP 326: Literales de cadena sin formato (versión preliminar). Los conceptos fueron reelaborados para producir el Bloques de texto característica en su lugar.
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:
Un CharSequence es una secuencia legible de valores char. Esta interfaz proporciona acceso uniforme de solo lectura a muchos tipos diferentes de secuencias de caracteres. Un valor char representa un carácter en el plano multilingüe básico (BMP) o un sustituto. Consulte Representación de caracteres Unicode para obtener más detalles.