Мой класс сущности выглядит так:
public class Student {
private int grade;
// other fields and methods
}
и использую вот так:
List<Student> students = ...;
Как можно отсортировать students по grade, учитывая, что это частное поле?
Я действительно стараюсь не использовать их, потому что обычно это плохая практика.
сеттер может быть .. но добытчик, не могу сказать, как
@Fanta, кто тебе сказал, что геттеры are usually a bad practice?
С каких это пор использование getters стало плохой практикой? Занимался разработкой на Java более 10 лет и никогда раньше не слышал этого утверждения.
Получатели / сеттеры @Popeye необходимы для DTO. Но с Бизнес-объекты они включают Особенности зависти и нарушения принципа скажи, не спрашивай!.
@Fanta читать об инкапсуляции
Я бы не назвал геттеры «плохой» практикой, но они по большей части бесполезны, потому что они не меньше и не больше являются связывающим контрактом, чем открытое поле без геттера. К сожалению, IDE часто активно наказывают вас за неиспользование этой избыточной конструкции (например, автоматический рефакторинг часто проверяет только геттеры, а не поля напрямую). Предполагаемые «преимущества», приписываемые добытчикам подавляющим большинством его экспертов, в основном бредовые.
@PallavKabra Я думаю, что вы тот, кто должен прочитать об этом. Объявление частных переменных, чтобы вы могли инкапсулировать свои данные, а затем использовать геттеры, все равно что прятать что-то в стеклянном ящике. Поле не является общедоступным, но способ его получения является открытым, поэтому вы буквально нарушаете свои концепции инкапсуляции.
@Fanta Но в этом случае вам действительно нужно свойство, так какой смысл скрывать его, если вы не хотите его скрывать? Конечно, вы можете написать метод, использующий частную переменную (как в ответах), но нет ничего плохого в использовании геттера. Этот сценарий, по сути, является функцией даже C#, где вы можете использовать свойство с общедоступным получателем и частным установщиком.




В общем, если вам нужно поведение, зависящее от оценки учащегося, эта информация должна быть доступна - добавьте метод или свойство, позволяющее другому коду получить к нему доступ.
Вот простейшее исправление:
public class Student implements IStudent {
...
private int grade;
...
// other fields and methods
public int getGrade() {
return grade;
}
}
Наверное, стоит расширить и интерфейс IStudent :)
Однако, если вам это нужно только для сортировки, вы можете пойти с идеей, уже предложенной в других ответах: реализовать интерфейс Comparable. Таким образом, вы можете скрыть grade и использовать его внутри метода int compareTo.
@TimothyTruckle Почему бы не оставить себе ответ о DTO? +1
@TimothyTruckle А почему это было бы неправильно для сущности?
@TimothyTruckle Я не понимаю, о чем ты. С точки зрения DDD, это совершенно разные миры. DTO, имеющие геттеры или сеттеры, зависят только от вашего инструментария / инфраструктуры - если они вам не нужны с технической точки зрения, вы не добавляете их. Для сущностей вы просто пытаетесь создать разумную объектную модель с включенными геттерами / сеттерами, которые имеют смысл с точки зрения бизнеса.
Геттеры - неплохая практика, они созданы именно для вашей проблемы: доступ к закрытым полям для их чтения. Добавьте геттер, и вы сможете:
studentsList.stream().sorted((s1, s2) -> s1.getGrade()compareTo(s2.getGrade)).collect(Collectors.toList())
Обновление: если вы действительно хотите сохранить частную оценку, вам необходимо реализовать Comparable и переопределить метод сравнения.
У вас есть следующие варианты:
grade видимымgradeComparatorвнутриStudentStudent реализовать ComparableПример решения 3:
public class Student {
private int grade;
public static Comparator<Student> byGrade = Comparator.comparing(s -> s.grade);
}
и используйте это так:
List<Student> students = Arrays.asList(student2, student3, student1);
students.sort(Student.byGrade);
System.out.println(students);
Это мое любимое решение, потому что:
ComparatorПример решения 4:
public class Student implements Comparable {
private int grade;
@Override
public int compareTo(Object other) {
if (other instanceof Student) {
return Integer.compare(this.grade, ((Student) other).grade);
}
return -1;
}
}
Вы можете отсортировать везде следующим образом:
List<Student> students = Arrays.asList(student2, student3, student1);
Collections.sort(students);
System.out.println(students);
Аспекты этого решения:
grade представляет естественный порядок студентов.TreeMap)Вы даже можете реализовать интерфейс Comparable
Мне нравится решение 3: умный пример функционального кода. Но почему вы называете это «определением внутреннего класса»?
@ user1708042 Ошибка - это мог быть внутренний класс, но нотация tge java-8 предпочтительнее. Я обновил свой ответ, спасибо!
Мне больше всего нравится вариант 3 - определить компаратор внутри ученика.
Ваш компаратор byGrade должен быть общедоступным, иначе вы не сможете его использовать. Пожалуйста, измените свой ответ.
Реализуйте Сопоставимый интерфейс для класса Student и реализуйте метод int compareTo(T o). Таким образом, вы можете оставить свойство Grade конфиденциальным.
Ваш класс мог бы реализовать интерфейс Comparable. Затем вы можете легко отсортировать список:
public class Student implements IStudent, Comparable<Student>
{
...
private int grade;
...
@Override
public int compareTo(Student other)
{
return (grade - other.grade);
}
}
public class Section
{
private List<IStudent> studentsList;
...
public void sortStudents()
{
studentsList.sort(null);
}
}
Другой вариант, который упоминался ранее, но не показан в качестве примера, - это реализация специального Comparator для сравнения по классам.
Этот пример состоит из класса Student, реализующего интерфейс IStudent, StudentGradeComparator и небольшого класса Main, который использует образцы данных.
Дальнейшие пояснения даются в виде комментариев к коду, прочтите их.
/**
* A class that compares students by their grades.
*/
public class StudentGradeComparator implements Comparator<IStudent> {
@Override
public int compare(IStudent studentOne, IStudent studentTwo) {
int result;
int studentOneGrade = studentOne.getGrade();
int studentTwoGrade = studentTwo.getGrade();
/* The comparison just decides if studentOne will be placed
* in front of studentTwo in the sorted order or behind
* or if they have the same comparison value and are considered equal
*/
if (studentOneGrade > studentTwoGrade) {
/* larger grade is considered "worse",
* thus, the comparison puts studentOne behind studentTwo
*/
result = 1;
} else if (studentOneGrade < studentTwoGrade) {
/* smaller grade is considered "better"
* thus, the comparison puts studentOne in front of studentTwo
*/
result = -1;
} else {
/* the students have equal grades,
* thus, there will be no swap
*/
result = 0;
}
return result;
}
}
Вы можете применить этот класс в методе sort(Comparator<? super IStudent> comparator)List:
/**
* The main class for trying out the sorting by Comparator
*/
public class Main {
public static void main(String[] args) {
// a test list for students
List<IStudent> students = new ArrayList<IStudent>();
// create some example students
IStudent beverly = new Student("Beverly", 3);
IStudent miles = new Student("Miles", 2);
IStudent william = new Student("William", 4);
IStudent deanna = new Student("Deanna", 1);
IStudent jeanLuc = new Student("Jean-Luc", 1);
IStudent geordi = new Student("Geordi", 5);
// add the example students to the list
students.add(beverly);
students.add(miles);
students.add(william);
students.add(deanna);
students.add(jeanLuc);
students.add(geordi);
// print the list in an unordered state first
System.out.println("———— BEFORE SORTING ————");
students.forEach((IStudent student) -> {
System.out.println(student.getName() + ": " + student.getGrade());
});
/*---------------------------------------*
* THIS IS HOW YOU APPLY THE COMPARATOR *
*---------------------------------------*/
students.sort(new StudentGradeComparator());
// print the list ordered by grade
System.out.println("———— AFTER SORTING ————");
students.forEach((IStudent student) -> {
System.out.println(student.getName() + ": " + student.getGrade());
});
}
}
Для полноты картины вот интерфейс IStudent и класс его реализации Student:
public interface IStudent {
String getName();
int getGrade();
}
/**
* A class representing a student
*/
public class Student implements IStudent {
private String name;
private int grade;
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
Опция, предоставляемая JDK 1.8, использует метод stream библиотеки sorted(), который не требует реализации интерфейса Comparable.
Вам необходимо реализовать метод доступа (получателя) для поля grade
public class Student {
private int grade;
public int getGrade() {
return grade;
}
public Student setGrade(int grade) {
this.grade = grade;
return this;
}}
Затем, имея unsortedStudentList, вы можете отсортировать его, как показано ниже:
List<Student> sortedStudentList = unsortedStudentList
.stream()
.sorted(Comparator.comparing(Student::getGrade))
.collect(Collectors.toList());
Кроме того, метод sorted() позволяет сортировать студентов по другим полям (если они есть). Например, рассмотрим поле name для ученика, и в этом случае вы хотите отсортировать список учеников по классу и имени. Итак, класс Student будет таким:
public class Student {
private int grade;
private String name;
public int getGrade() {
return grade;
}
public Student setGrade(int grade) {
this.grade = grade;
return this;
}
public String getName() {
return name;
}
public Student setName(String name) {
this.name = name;
return this;
}}
Чтобы отсортировать по обоим полям:
List<Student> sortedStudentList = unsortedStudentList
.stream()
.sorted(Comparator.comparing(Student::getGrade)
.thenComparing(Comparator.comparing(Student::getName)))
.collect(Collectors.toList());
Второй компаратор вступает в игру, когда первый сравнивает два равных объекта.
Если вам действительно нужно сортировать по полю, к которому у вас нет доступа, вы можете использовать отражение:
private static int extractGrade(Student student) {
try {
Field field = Student.class.getDeclaredField("grade");
field.setAccessible(true);
return field.getInt(student);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
Comparator<Student> studentComparator = Comparator.comparingInt(DemoApplication::extractGrade);
List<Student> students = Arrays.asList(new Student(1), new Student(10), new Student(5));
students.sort(studentComparator);
}
Но я должен сказать, что этот метод небезопасен.
Не используйте его без крайней необходимости. Лучше предоставить доступ к данному полю, например, с помощью метода получения.
Также у вас могут возникнуть проблемы, если вы запускаете этот код в пути к модулю для Java 9+ (вы можете получить выброс InaccessibleObjectException).
ComparableИз Comparable документы:
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's {@code compareTo} method is referred to as its natural comparison method.
Но что может быть естественный порядок для Student?
Имя? Фамилия? Их сочетание?
На этот вопрос легко ответить для чисел, но не для таких классов, как Student.
Поэтому я не думаю, что Student должны быть Comparable, это люди, а не даты или числа. И нельзя сказать, кто больше, кто равен, а кто меньше.
Я считаю, что когда вы хотите отсортировать любой класс по определенным мною характеристикам, таким как оценка, в этом примере он становится сопоставимым кандидатом. Дело в том, что в объекте существует более одного типа сортировки. В таком случае никакая сортировка не будет естественный, и я бы не стал использовать интерфейс для этого - в этом случае у меня было бы поле Comparator в классе, реализующем функцию сравнения.
Я считаю, что лучшим вариантом здесь является создание Comparator, где необходим отсортированный список, потому что вам может потребоваться сортировка по другим полям в других местах, и это приведет к раздутию вашего класса домена:
List<Student> sorted = list.stream()
.sorted(Comparator.comparingInt(o -> o.grade))
.collect(Collectors.toList());
Вы можете сделать это так, если хотите, чтобы оценка оставалась конфиденциальной:
students = students.stream().sorted((s1, s2) -> {
try {
Field f = s1.getClass().getDeclaredField("grade");
f.setAccessible(true);
int i = ((Integer)f.getInt(s1)).compareTo((Integer) f.get(s2));
f.setAccessible(false);
return i;
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
return 0;
}).collect(Collectors.toList());
у вас есть геттер для этого поля?