java.lang.String is declared as final, however are there any mechanisms available legitimate or otherwise to extend it and replace the equals(String other) method?
No, absolutely not. If you want some "other" kind of string, create another type which might contain a string:
public final class OtherString {
private final String underlyingString;
public OtherString(String underlyingString) {
this.underlyingString = underlyingString;
}
// Override equals however you want here
}
I guess the closest you can come is making some class that implements CharSequence. Most JDK string manipulation methods accept a CharSequence. StringBuilder for example. Combined with a good implementation of toString(), the various methods of String and valueOf(), you can come pretty close to a natural substitute.
Now, there is a way. With manifold it's possible to extend every Java Class. Here is an example for String:
package extensions.java.lang.String;
import manifold.ext.api.*;
#Extension
public class MyStringExtension {
public static void print(#This String thiz) {
System.out.println(thiz);
}
#Extension
public static String lineSeparator() {
return System.lineSeparator();
}
}
Can than be used as follow:
String name = "Manifold";
name.print();
String.lineSeparator();
Another example can be found here: https://jaxenter.com/manifold-code-generator-part-2-151762.html
Notice, that manifold is still alpha.
You cannot extend a class that is marked as final. You can use composition to either put a String object inside or you can hand roll your own version. This can be accomplished via character arrays and the other magic that goes into creating String classes.
It is not possible to directly inherit String class as it is final. Also wrapper classes java.lang.Integer, java.lang.Float, etc... are final.
I wrote a simple Strings class that mimics java.lang.String and can be extended. This is just to demonstrate the core implementation requirements behind String are not "mysterious" or "complicated". And, you can use it to do things like create a password extension and manage strings more effectively.
Needs:
native call to big-endian. (see private static native boolean
isBigEndian();)
other String methods.
package com.paintedintel.util;
import java.nio.charset.StandardCharsets;
/**
*
* #author David Urry
* #date 2/8/2020
*
* Strings is a light weight string implementation based on StringUTF16.
* It's sole purpose is to create a system where strings can be extended
* so that the type of string can be extended without the weight of carrying
* extra object references.
*
* Strings extension is important for 2 reasons:
* 1) The extra object reference slows the code down by a factor of 50% making
* a 10X speed improvement only 5X. As the object of looking at/comparing and
* otherwise managing strings is expensive.
* 2) The code understanding benefits greatly from understanding the type of string
* you are working with (Name, Value, Field, InitValue, Comment...). The constant
* evaluation of List<String> for example is greatly simplified when observing
* List<Field> instead.
*
* This problem was also greatly simplified by working with Type, Domain, Datum,
* StreamDomain, StreamCase and other objects as complex objects.
*/
public class Strings {
final byte[] value;
/** Cache the hash code for the string */
private int hash; // Default to 0
protected Strings(String value){
if (value != null) {
this.value = value.getBytes();
this.hash = value.hashCode();
} else {
this.value = new byte[0];
this.hash = 0;
}
}
Strings(byte[] value){
this.value = value;
this.hash = Strings.hashCode(value);
}
#Override
public String toString() {
return new String(value, StandardCharsets.UTF_8);
}
public String str() {
return toString();
}
public boolean equals(String str) {
return (str == null)?((value == null || this.length() == 0)?true:false):str.hashCode() == value.hashCode();
}
public boolean eq(String str) {
return equals(str);
}
byte[] getBytes() {
return value;
}
int getHash() {
return hash;
}
public int length() {
return value.length >> 1;
}
/**
* this is based on StringUTF16
* #param value
* #return
*/
synchronized public static int hashCode(byte[] value) {
int h = 0;
int length = value.length >> 1;
for (int i = 0; i < length; i++) {
h = 31 * h + getChar(value, i);
}
return h;
}
// intrinsic performs no bounds checks
synchronized static char getChar(byte[] val, int index) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
index <<= 1;
return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
((val[index] & 0xff) << LO_BYTE_SHIFT));
}
//private static native boolean isBigEndian();
// private static boolean isBigEndian() {
// //as of 2018 there are no major BigEndian systems left.
// // This is because it's less processing to convert & work with
// // Little-Endian.
// return false;
// }
static final int HI_BYTE_SHIFT = 0;
static final int LO_BYTE_SHIFT = 8;
// static {
// if (isBigEndian()) {
// HI_BYTE_SHIFT = 8;
// LO_BYTE_SHIFT = 0;
// } else {
// HI_BYTE_SHIFT = 0;
// LO_BYTE_SHIFT = 8;
// }
// }
synchronized public static int length(byte[] value) {
return value.length >> 1;
}
}
Related
I want the last enum to have a different value in one of the variables:
private enum thing {
thing0(0),
thing1(1),
thing2(2);
int index;
String s;
private thing(int index) {
this.index = index;
s = index == values().length - 1 ? "b" : "a";
}
}
This doesn't work; you can't call values() in the constructor. Is there another way?
In general, don't rely on the declaration order of the enum values. Item 35 in Effective Java 3rd Ed, "Use instance fields instead of ordinals", explains why. (Note that whilst you are using an instance field for s, its value depends on the ordinal.)
If you want a particular value to have a particular property, pass it in as a constructor parameter.
private enum thing {
thing0(0),
thing1(1),
thing2(2, "b");
int index;
String s;
private thing(int index) {
this(index, "a");
}
private thing(int index, String s) {
this.index = index;
this.s = s;
}
}
If you really do want it to be checking for the last value in the enum, an alternative way to do this is with a getter. Initialize a static final field in the enum to be the last value:
// Invokes `values()` twice, but meh, it's only executed once.
private static final thing LAST = values()[values().length-1];
Then check in a getter:
String s() {
return this == LAST ? "b" : "a";
}
There is no need to maintain an index that always matches the ordinal of the enum constant. Further, you can’t rely on the values() array in the constructor as it is supposed to contain the already constructed instances. But to determine, how many constants exist, it is enough to count the associated fields.
public enum Thing {
thing0,
thing1,
thing2;
final String s;
Thing() {
this.s = ordinal() == numConstants() - 1? "b": "a";
}
#Override
public String toString() {
return name() + "(index = " + ordinal() + ", s = " + s + ")";
}
private static int NUM_CONSTANTS;
private static int numConstants() {
int i = NUM_CONSTANTS;
if(i != 0) return i;
for(Field f: Thing.class.getDeclaredFields()) if(f.isEnumConstant()) i++;
NUM_CONSTANTS = i;
return i;
}
}
So System.out.println(EnumSet.allOf(Thing.class)); prints
[thing0(index = 0, s = a), thing1(index = 1, s = a), thing2(index = 2, s = b)]
Note that numConstants() caches the value in NUM_CONSTANTS which is safe as the private method is only invoked within the class initializer. We can’t use a static final variable here, as all custom class initialization will be done after the enum constants have been constructed.
I'm still a little confused with regards to the difference between static and dynamic. From what I know dynamic uses object while static use type and that dynamic is resolved during runtime while static is during compile time. so shouldn't this.lastName.compareTo(s1.lastName) use dynamic binding instead?
key.compareTo(list[position-1]) use dynamic binding
public static void insertionSort (Comparable[] list)
{
for (int index = 1; index < list.length; index++)
{
Comparable key = list[index];
int position = index;
while (position > 0 && key.compareTo(list[position-1]) < 0) // using dynamic binding
{
list[position] = list[position-1];
position--;
}
list[position] = key;
}
}
Why does (this.lastName.compareTo(s1.lastName)) use static binding?
private String firstName;
private String lastName;
private int totalSales;
#Override
public int compareTo(Object o) {
SalePerson s1 = (SalePerson)o;
if (this.totalSales > s1.getTotalSales())
{
return 1;
}
else if (this.totalSales < s1.getTotalSales())
{
return -1;
}
else //if they are equal
{
return (this.lastName.compareTo(s1.lastName)); //why is this static binding??
}
}
Your question isn't complete and doesn't include all relevant the code. However this is the basic difference between the different bindings
Java has both static and dynamic binding. Binding refers to when variable is bound to a particular data type.
Static/Early binding is done at compile time for: private, final and static methods and variables. And also for overloaded methods
Dynamic/late binding is done at runtime for: methods which can be overriden methods. This is what enables polymorphic behaviour at runtime.
To further demonstrate this point have a look at this code and see if you can determine when it would be early and late binding:
/* What is the output of the following program? */
public class EarlyLateBinding {
public boolean equals(EarlyLateBinding other) {
System.out.println("Inside of overloaded Test.equals");
return false;
}
public static void main(String[] args) {
Object t1 = new EarlyLateBinding(); //1
Object t2 = new EarlyLateBinding(); //2
EarlyLateBinding t3 = new EarlyLateBinding(); //3
Object o1 = new Object();
Thread.currentThread().getStackTrace();
int count = 0;
System.out.println(count++);
t1.equals(t2);//n
System.out.println(count++);
t1.equals(t3);//n
System.out.println(count++);
t3.equals(o1);
System.out.println(count++);
t3.equals(t3);
System.out.println(count++);
t3.equals(t2);
}
}
Answer:
++ is after the count and hence the result returned is the 0 before incrementing it. Hence starts with 0 and proceeds as you expect.
The only scenario where the equals methods of EarlyLateBinding object
is actually invoked is is statement 3.
This is because the equals method is overloaded (Note: the different
method signature as compared to the object class equals)
Hence the type EarlyLateBinding is bound to the variable t3 at
compile time.
.
in this code
public static void insertionSort (Comparable[] list)
{
for (int index = 1; index < list.length; index++)
{
Comparable key = list[index];
int position = index;
while (position > 0 && key.compareTo(list[position-1]) < 0)
{
list[position] = list[position-1];
position--;
}
list[position] = key;
}
}
key can be anything that implements the Comparable interface so in the compile time compiler doesn't know the exact type so type is resolved in the runtime by using the object that key referring to.
But in this code,
#Override
public int compareTo(Object o) {
SalePerson s1 = (SalePerson)o;
if (this.totalSales > s1.getTotalSales())
{
return 1;
}
else if (this.totalSales < s1.getTotalSales())
{
return -1;
}
else //if they are equal
{
return (this.lastName.compareTo(s1.lastName));
}
}
compiler knows the type of the s1 so it use the static binding
I have a class exactly like this:
public class Operator {
private String oper;
private boolean ltr;
private int pc;
//Ignore these methods...
public Operator(String t,int prc,boolean as) {oper=t;pc=-prc;ltr=as;}
public int precedence() {return pc;}
public boolean associativity() {return ltr;}
public String getName() {return oper;}
public int hashCode() {
int hash = 3;
hash = 19 * hash + (this.oper != null ? this.oper.hashCode() : 0);
hash = 19 * hash + (this.ltr ? 1 : 0);
hash = 19 * hash + this.pc;
return hash;
}
public boolean equals(Object o){
if (o instanceof String){
return oper.equals(o);
}
return false;
}
public String toString(){
return oper;
}
}
when I do: System.out.println(new Operator("+",4,true).equals("+")); it prints true, which means that equals method is working.
but when I do this:
Vector oprs = new Vector();
oprs.addElement(new Operator("+",4,true));
int iof = oprs.indexOf("+");
System.out.println(iof);
iof is -1. Manual searching finds it well, and System.out.println(oprs.elementAt(0)); prints +. I thought indexOf uses equals method to find the element (like in Java SE) so why on earth oprs.indexOf isn't working?
The type of "+" is String. You can't redefine equality for the String class so your equals method is not reflexive. Check out the Comparator class. It (and the collection classes that use it) might help you.
What I do not understand is why I am getting an error compiling my code when a String is in fact an object, and the compiler is saying otherwise. I dont know why I keep getting this error message
symbol: method compareTo(Object)
location: variable least of type Object
.\DataSet.java:17: error: cannot find symbol
else if(maximum.compareTo(x) < 0)
here is the code. I'm trying to utilize the class comparable to allow two objects to use the compareTo method. In the tester, I'm just trying to use a basic string object to compare.
public class DataSetTester
{
public static void main(String[] args)
{
DataSet ds = new DataSet();
String man = "dog";
String woman = "cat";
ds.add(man);
ds.add(woman);
System.out.println("Maximum Word: " + ds.getMaximum());
}
}
Class:
public class DataSet implements Comparable
{
private Object maximum;
private Object least;
private int count;
private int answer;
public void add(Object x)
{
if(count == 0){
least = x;
maximum = x;
}
else if(least.compareTo(x) > 0)
least = x;
else if(maximum.compareTo(x) < 0)
maximum = x;
count++;
}
public int compareTo(Object anObject)
{
return this.compareTo(anObject);
}
public Object getMaximum()
{
return maximum;
}
public Object getLeast()
{
return least;
}
}
Comparable Interface:
public interface Comparable
{
public int compareTo(Object anObject);
}
Of course String is an Object.
Comparable is generic now. Why do you feel the need to make those references Object if they are type String? Your code is poor; it's not a Java problem.
I don't see why DataSet needs to implement Comparable. You just need to compare incoming Strings as they're added. Do it this way and you'll fare better:
public class DataSet {
private String maximum;
private String least;
private int count;
private int answer;
public void add(String x) {
if(count == 0){
least = x;
maximum = x;
} else if (least.compareTo(x) > 0) {
least = x;
} else if(maximum.compareTo(x) < 0) {
maximum = x;
}
count++;
}
public String getMaximum() { return this.maximum; }
public String getLeast() { return this.least; }
public int getCount() { return this.count; }
}
The problem is that DataSet implements Comparable, but Object doesn't.
Instead of storing Objects, you want to store Comparables. However, if you do get this to compile, you will get into an infinite loop right here:
public int compareTo(Object anObject)
{
// Yeah, never stop loopin'!
return this.compareTo(anObject);
}
It's recommended that in newer code, you use the generic Comparable<T> interface. Your code would then look like this:
public class DataSet implements Comparable<DataSet>
{
private String maximum;
private String least;
private int count;
private int answer;
public void add(String x)
{
if(count == 0){
least = x;
maximum = x;
}
else if(least.compareTo(x) > 0)
least = x;
else if(maximum.compareTo(x) < 0)
maximum = x;
count++;
}
public int compareTo(DataSet anObject)
{
// I don't really know how you want this to work.
// Come up with your own criteria on what makes a DataSet greater or less than
// another one.
count - anObject.count
}
// Good practice to include this if you're doing a compareTo.
#Override
public boolean equals(Object other)
{
return (other instanceof DataSet) && compareTo((DataSet)other) == 0;
}
public String getMaximum()
{
return maximum;
}
public String getLeast()
{
return least;
}
}
Edit - just saw that you're comparing strings. In that case, you don't really need DataSet to implement Comparable. However, if you do need it for something else, what I wrote still stands.
least and maximum are simply Objects, and the Object class doesn't have a compareTo(...) method, simple as that. least and maximum need to be declared Comparable, not Object. And as written, it makes no sense declaring DataSet to implement the Comparable interface since there are no DataSet objects present and certainly none being compared.
java.lang.Object does not have a compareTo() method.
First of all there is an infinite loop in you code:
public int compareTo(Object anObject)
{
return this.compareTo(anObject);
}
this method is continuously calling itself.
Regarding your compile error: you have declared variable as Object, which obviously does not have a compareTo method.
There is no compareTo() method in Object. I guess you're looking for String.compareTo().
Type checking is done at compile time and not runtime. At compile time, least and maximum are considered to be objects of type Object and not String.
This is what I have so far but I don't now what to do next. The question is as follows (sorry the coding is not all appearing in one box):
Implement a method
public void search (String searchString) { }
to iterate through the notes ArrayList until it finds a note that contains the searchString. It should then print either the item found or the message "String not found". When testing check for a String that is in the list and for one that isn't.
Code:
import java.util.ArrayList;
import java.util.Iterator;
/**
* A class to maintain an arbitrarily long list of notes.
* Notes are numbered for external reference by a human user.
* In this version, note numbers start at 0.
*
* #author David J. Barnes and Michael Kolling.
* #version 2008.03.30
*/
public class Notebook
{
// Storage for an arbitrary number of notes.
private ArrayList<String> notes;
/**
* Perform any initialization that is required for the
* notebook.
*/
public Notebook()
{
notes = new ArrayList<String>();
}
/**
* Store a new note into the notebook.
* #param note The note to be stored.
*/
public void storeNote(String note)
{
notes.add(note);
}
/**
* #return The number of notes currently in the notebook.
*/
public int numberOfNotes()
{
return notes.size();
}
/**
* Show a note.
* #param noteNumber The number of the note to be shown.
*/
public void showNote(int noteNumber)
{
if(noteNumber < 0) {
// This is not a valid note number, so do nothing.
System.out.println("invalid index given");
}
else if(noteNumber < numberOfNotes()) {
// This is a valid note number, so we can print it.
System.out.println(notes.get(noteNumber));
}
else {
System.out.println("there are fewer items in the notebook than that");
// This is not a valid note number, so do nothing.
}
}
public void removeNote(int noteNumber)
{
if(noteNumber < 0) {
// This is not a valid note number, so do nothing.
System.out.println("invalid index given");
}
else if(noteNumber < numberOfNotes()) {
// This is a valid note number.
notes.remove(noteNumber);
}
else {
System.out.println("there are fewer items in the notebook than that");
// This is not a valid note number, so do nothing.
}
}
public void multiplesOfFive()
{
int i = 10;
while(i < 100)
{
System.out.println(i);
i = i + 5;
}
}
public int sum(int a, int b)
{
int index = a;
int result = 0;
while(index <= b)
{
result = result + index;
index = index + 1;
}
return result;
}
public int product(int a, int b)
{
int index = a;
int result = 1;
while(index <= b)
{
result = result * index;
index = index + 1;
}
return result;
}
public boolean
isPrime (int n)
{
if (n<=1)return false;
if (n==2) return true;
for (int i = 2;i<=n-1;i++)
{
if (n%i==0)return false;
}
return true;
}
}
two ideas to consider:
When you compose your search method, consider utilizing the contains method in the String class as you iterate (see Kaleb Brasee's post).
ensure that you handle the case when a null is passed in as the search param.
Use one of the new for-each style loops to iterate over the List of notes:
for (String string : notes) {
// This will loop over all the Strings in the notes List.
// Perform your logic here.
}
If the list is not in alphabetical order you need to loop through the list comparing each string against the search string. Once you find a match you can break the loop (using a return true (or the string) would be the easiest way) then outside the loop you can place a return false to signify that a match was not found.
Some methods you will need to use:
ArrayList:
size() - gives you the size of the list so you know when you have reached the end
get( int index ) - returns the item in the list at the specified index
String: equals( String cmp ) - compares 2 strings and returns an int
It would be good to become familiar with the Java API so that you can find methods and their return values.
If the list is in alphabetical order there are more efficient ways to search.