【问题标题】:How to figure out what object is in a abstract list?如何找出抽象列表中的对象?
【发布时间】:2019-11-28 01:28:18
【问题描述】:

我有一个名为会话的抽象类。讲座和教程延长了会议。然后我有一个名为注册的课程,其中包含课程列表(讲座和教程)。如何在 Enrollment 中遍历会话列表并仅从会话列表中返回 Lectures 列表?

我的下一个问题是我是否应该存储 2 个列表。一个讲座列表和一个教程列表,而不是 1 个会话列表?这是因为会话列表对我来说毫无用处,我每次都必须遍历它以获取有关讲座和教程的信息。有没有办法让我得到所有的讲座对象?我是java新手。

public class Enrolment {

    private List<Session> sessions;

    public Enrolment() {
        this.sessions = new ArrayList<>();
    }

    public addSession(Session session) {
        this.sessions.add(session);
    }
}

public class Session {

    private int time;

    public Session(int time) {
        this.time = time;
    }
}

public class Lecture extends Session {

    private String lecturer;

    public Lecture(int time, String lecturer) {
        super(time);
        this.lecturer = lecturer;
    }
}

public class Tutorial extends Session {

    private String tutor;
    private int tutorScore;

    public Tutorial(int time, String tutor, int tutorScore) {
        super(time);
        this.tutor = tutor;
        this.tutorScore = tutorScore;
    }

}

public class test {
    public static void main(String[] args) {
        Enrolment newEnrolment = new Enrolment();

        Lecture morningLec = new Lecture(900, "Dr. Mike");
        newEnrolment.addSession(morningLec);

        Tutorial afternoonTut = new Tutorial(1400, "John Smith", 3);
        newEnrolment.addSession(afternoonTut);

        Lecture middayLec = new Lecture(1200, "Mr. Micheals");
        newEnrolment.addSession(middayLec);

        Tutorial NightTut = new Tutorial(1900, "Harry Pauls", 4);
        newEnrolment.addSession(NightTut);
    }
}

【问题讨论】:

  • 我应该存储 2 个列表。一份讲座清单和一份教程清单...:也许问题应该是:你真的需要Session 类本身吗?
  • @ernest_k 讲课对象和教程对象都有时间。而Session 基本上是不重复时间的东西(拥有一个抽象类是有意义的)。
  • 在所有实例中,复制并非总是不好。有时这是一个很好的权衡。如果所有 Session 抽象增加的是您现在面临的复杂性,那么也许应该质疑它。
  • 我同意 ernesk_k。有充分的理由拥有一个抽象类并不意味着你会在每个地方都使用它,即使你不想要这个抽象。您不会创建Object 的列表,因为您可以这样做,但因为您需要。您的Session 列表也是如此。

标签: java list loops class arraylist


【解决方案1】:

流式传输sessions 列表并使用instanceof 过滤Lectures 类型的对象

List<Lecture> l = sessions.stream()
                           .filter(Lecture.class::isInstance) 
                           .map(Lecture.class::cast)                               
                           .collect(Collectors.toList());

通过使用for 循环,为每种类型使用两个不同的列表

List<Lecture> l = new ArrayList<>();
List<Tutorial> t = new ArrayList<>();
for (Session s : sessions) {
    if (s instanceof Lecture) {
        l.add((Lecture) s);
    }
      else if(s instanceof Tutorial) {
        t.add((Tutorial) s);
    }
}

【讨论】:

  • 我编辑了我的问题,你能看看我第二段的问题吗?您还可以告诉我如何在 for 循环或 while 循环中执行此操作吗?还有你提供的那条线,是 O(n) 还是 O(1) 的时间复杂度?
  • 如果不将map 转换为Lecture,我认为它不会起作用。
  • @michalk 所以我的问题是关于会话列表的。是否有某种标准程序员用来获取特定对象。会话类本身是无用的。它什么也不做。我从不需要会话类,我总是需要 Lecture 对象和 Tutorial 对象。有没有标准的程序员用来只处理列表中的特定对象,比如会话
  • 您可以(而且恕我直言应该)使用.filter(Lecture.class::isInstance) .map(Lecture.class::cast) 而不是instanceof 和(Hard) 演员表。然后你也可以很好地把它拉到一个实用方法中并像List&lt;Lecture&gt; lectures = get(sessions, Lecture.class);一样调用它。
【解决方案2】:

在class Enrolment 中将private List&lt;Session&gt; sessions; 更改为public List&lt;Session&gt; sessions;

public static void main(String[] args) {
    ....
    var lecturesAndTutorials = newEnrolment.sessions.where(x => x.getType() == typeof(Lecture) || x.getType() == typeof(Tutorial));
    ....
}

【讨论】:

    【解决方案3】:
            List<Lecture> l = newEnrolment.getSessions()
                .stream()
                .filter(s-> s.getClass().equals(Lecture.class))
                .map(session -> (Lecture) session)
                .collect(Collectors.toList());
    

    !!!不要使用 typeof

    【讨论】:

    • 除了过滤器表达式略有不同之外,这与死侍的答案有何不同? typeof 是什么?我猜你的意思是instanceof?为什么不使用它?
    • 使用 instanceof 是一种不好的做法。那只是一个区别。您也可以阅读> stackoverflow.com/questions/2750714/… 。当然,对不起,是的,我提到了 instanceof
    【解决方案4】:

    也许你应该存储在两个列表中,就像:

    public class Enrolment {
    
        private List<Lecture> lectures;
        private List<Tutorial> tutorials;
    
        public Enrolment() {
            this.lectures = new ArrayList<>();
            this.tutorials = new ArrayList<>();
        }
    
        public void addSession(Session session) {
            if (session instanceof Lecture) {
                lectures.add((Lecture) session);
            } else if (session instanceof  Tutorial) {
                tutorials.add((Tutorial) session);
            }
        }
    
        public List<Lecture> getLectures() {
            return lectures;
        }
    
        public List<Tutorial> getTutorials() {
            return tutorials;
        }
    
        public List<Session> getAllSessions() {
            ArrayList<Session> sessions = new ArrayList<>(lectures);
            sessions.addAll(tutorials);
            return sessions;
        }
    }
    

    这是你需要的吗?

    【讨论】:

      【解决方案5】:

      引用你的问题,

      这是因为会话列表对我来说毫无用处。

      所以,这可能不是放置列表的正确位置(?)。

      我的偏好是拥有


      public interface Enrolment {
          public abstract addSession(Session session);
          public abstract getSessions();
      }
      

      并且List&lt;LectureEnrolment&gt; 和List&lt;TutorialEnrolment&gt; 必须在各自的类中。(我已将Lecture 重命名为LectureEnrolment 并将Tutorial 重命名为TutorialEnrolment)

      main() 必须有类似的东西,

      Enrolment lectureEnrolment= new LectureEnrolment()

      Enrolment tutorialEnrolement = new TutorialEnrolment()

      根据需要调用相应的addSession() 或getSession()。

      【讨论】:

        【解决方案6】:

        我的下一个问题是我是否应该存储 2 个列表。一份清单 讲座和一个教程列表,而不是 1 个会话列表?这是 因为会话列表对我没用,我必须循环 它每次都可以获取有关讲座和教程的信息。有没有 我缺少一种获取所有讲座对象的方法?

        你自己回答了你的问题。
        当您开始编写过于复杂/样板代码以使事情变得简单时,例如迭代您刚刚添加的对象列表时,这表明您应该退后一步并重新设计事物。

        通过介绍Enrolment.addSession(Session session), 你引入了一个不受欢迎的抽象:

        public class Enrolment {
        
            private List<Session> sessions;
        
            public Enrolment() {
                this.sessions = new ArrayList<>();
            }
        
            public addSession(Session session) {
                this.sessions.add(session);
            }
        }
        

        您不想从Enrolment 的角度统一处理Lecture 和Tutorial,所以不要将它们合并到同一个List 中,因为它们依赖于相同的接口(@ 987654327@).
        必须在需要时使用抽象,而不是系统地使用抽象,因为这是可能的。
        你不是在 List 中添加所有对象,因为所有对象都是 Object 吗?没有。

        而不是从 API 方法和它的实现创建这种区别:

        public class Enrolment {
        
            private List<Conference> conferences = new ArrayList<>();
            private List<Tutorial> tutorials = new ArrayList<>();
        
        
            public addConference(Conference conference) {
                this.conferences.add(conference);
            }
        
            public addTutorial(Tutorial tutorial) {
                this.tutorials.add(tutorial);
            }
        }
        

        并使用它:

        Lecture morningLec = new Lecture(900, "Dr. Mike");
        newEnrolment.addLecture(morningLec);
        
        Tutorial afternoonTut = new Tutorial(1400, "John Smith", 3);
        newEnrolment.addTutorial(afternoonTut);
        

        请注意,您可能会遇到这样一种情况,即您需要统一处理 Tutorial 和 Lecture 以进行某些处理,但对于其他处理则需要区分它们。
        在这种情况下,你有一些常用的方法:

        • instanceOf :易于使用但也容易使代码变脆。例如,稍后您可以在 Session 层次结构中添加一个新的子类,并且在不知道的情况下,该子类的实例可能会在某些处理中包含或排除,而编译器不会警告您。

        • 提供一个抽象方法,该方法返回一个布尔值或枚举值来传达对象的性质(例如:isLecture())。比instanceOf 更健壮,因为编译器限制您重写该方法,但如果添加多个子类并且过滤器不仅在Lecture 上,而且在Lecture 和另一种类型上,它也可能导致容易出错的代码。因此,在过滤条件保持简单的情况下,我会喜欢这种方式。

        • 定义三个列表:一个用于讲座,另一个用于会议,另一个包含所有这些应统一处理的列表。更复杂的方式,但也更健壮的方式。我只支持过滤条件复杂/变化的情况。

        【讨论】:

        • 完全正确 - 我感觉大多数 Java 教义完全忘记了 KISS 作为原则。大多数情况下,您不需要继承和所有花哨的东西,在解决问题的同时尽可能保持简单。
        猜你喜欢
        • 2017-04-30
        • 1970-01-01
        • 2021-08-23
        • 1970-01-01
        • 1970-01-01
        • 2010-12-23
        • 2012-11-11
        • 2019-12-13
        • 1970-01-01
        相关资源
        最近更新 更多