One option is to have the classes implement a common interface:
interface NamedVehicle {
String getName();
}
class Car implements NamedVehicle {
public String name;
@Override public String getName() { return name; }
}
class Bus implements NamedVehicle {
public String name;
@Override public String getName() { return name; }
}
And then store a list of interface references instead of Object:
final List<NamedVehicle> vehicles = new ArrayList<>(Arrays.asList(
new Car("Fiat"),
new Car("Citroen"),
new Bus("Ford"),
new Bus("Toyota")
));
vehicles.sort(Comparator.comparing(NamedVehicle::getName));
The above would be the recommended option (keywords "program against an interface", "information hiding", "don't expose fields"). If you cannot introduce an interface, it is going to be more tricky and ugly, but it's doable.
final List<Object> vehicles = new ArrayList<>(Arrays.asList(
new Car("Fiat"),
new Car("Citroen"),
new Bus("Ford"),
new Bus("Toyota")
));
vehicles.sort(Comparator.comparing(o -> {
if (o instanceof Car) { return ((Car)o).name; }
if (o instanceof Bus) { return ((Bus)o).name; }
return ""; // you cannot guarantee that the list will only contain Buses and Cars (it is <Object>, after all), so you have to return some dummy value here or throw an exception.
}));